DEV Community

loading...

Custom Elements Everywhere for Page Layout, Parts I and II

jaredcwhite profile image Jared White Originally published at bridgetownrb.com ・11 min read

Part I

As we enter the year 2021, allow me to make a bold statement: it's totally unnecessary—perhaps even an anti-pattern—to use <div> and <span> tags in your HTML.

Now before you shake your fist at your smartphone or computer screen and write me off as a veritable nutcase, let us briefly review the history of HTML.

How We Got Here

HTML stands for "HyperText Markup Language". It was originally designed as a text format for sharing academic materials. It had virtually no styling abilities, and in fact didn't even ship with a proper image tag.

Throughout the 1990s, as Netscape rose to prominence and the internet transitioned from higher academia to the public stage, the race was on to make HTML ever more capable in terms of visuals and interactivity.

HTML gave way to DHTML—Dynamic HyperText Markup Language. Netscape had their own idea of how DHTML should work, called the LAYER tag. But that tag never achieved widespread adoption and instead we ended up with a potent combination of CSS and…the DIV tag.

From the HTML4 introduction to DIV and SPAN dating from 1999:

The DIV and SPAN elements, in conjunction with the id and class attributes, offer a generic mechanism for adding structure to documents. These elements define content to be inline (SPAN) or block-level (DIV) but impose no other presentational idioms on the content. Thus, authors may use these elements in conjunction with style sheets, the lang attribute, etc., to tailor HTML to their own needs and tastes.

Suppose, for example, that we wanted to generate an HTML document based on a database of client information. Since HTML does not include elements that identify objects such as "client", "telephone number", "email address", etc., we use DIV and SPAN to achieve the desired structural and presentational effects. We might use the TABLE element as follows to structure the information…

While it's true you could use <div> tags in the late 90s, it was slow going in terms of page layout because of the limitations of CSS itself. Many website designs used tables instead, or even resorted to proprietary plugins such as Flash.

But eventually the industry transitioned away from tables, Flash, etc. to "float-based" layouts (and since flexbox and grid)—and the tag most commonly used to hang styles off of remained the most generic tag of all: DIV.

However, that's not the end of our story! Concurrent with the developing power of CSS to handle complex page layout was the development of XHTML. Everyone in the decade of the 2000s went bananas over XML, and the web community was no different. XHTML was a change that saw HTML switch from being based on SGML (Standard Generalized Markup Language) to being based on XML (eXtensible Markup Language). XML was envisioned as providing a newer/easier/better method of defining new document types/schemas than its predecessor SGML, and one of the benefits of XML provided to XHTML was the ability to mix in other XML schemas. In other words, you could extend HTML itself. (Read this W3C documentation on XHTML modularity.)

Yes indeed, you could add new elements to XHTML using externally-defined DTDs (Document Type Definitions), turning HTML itself into nothing more than one building block among many other building blocks for a bigger, more expressive, more structured, semantic web.

Only that never happened.

XHTML was eventually rejected as The One True Path Forward for the web. In its place, we got HTML5, and HTML5 defined the decade of the 2010s.

HTML5 eventually did away with all its past XML-ness and instead concentrated on making HTML as balanced as possible between familiarity and expressiveness while establishing a clear process for cross-browser collaboration going forward in many related areas (namely, CSS & Javascript).

Unfortunately, in weaning web developers back off of XML, the modular nature of XML largely was lost, and thus enthusiasts of expressive, semantic markup were relegated to the fringe edges of hypertext theory.

Or were they?

Introducing Custom Elements

While everyone was rocking their DIVs and SPANs for accomplishing, well, pretty much anything and everything in HTML page layout, a curious new spec emerged—largely in parallel to HTML5 itself.

First drafted in 2013, the Custom Elements spec defined how browsers could "upgrade" a custom element (recognizable by its hypenated nature…i.e. <my-tag>, rather than simply <tag>) and provide it with special interactive powers. The curious thing about the Custom Elements spec is it basically just talks about Javascript and hardly touches on the HTML side of things itself. Why is that?

Because browsers already allowed virtually unlimited tag names!

Sure, it wasn't "valid" HTML by any means, and you might occasionally run into the odd side-effect, but because browsers were always very liberal about how they interpreted an HTML document, there wasn't really anything stopping you from using <paragraph> instead of <p> or <navbar> instead of <nav>.

Nevertheless, the Custom Elements spec officially "blessed" the usage of custom elements and provided them with full Javascript-based lifecycles. As long as you have at least one hyphen in the tag name, you're golden. Over time, that spec evolved into the advanced Web Components spec now supported by all modern browsers.

What apparently has been lost on many web developers is: you don't need to use Javascript to use Custom Elements. It's optional.

That's right, all that's required to add a custom element to your HTML5 document is…to add it. 😄 Here's how you do it:

Take a bare HTML5 page:

<!doctype html>
<html>
  <head>
    <title>My HTML Page</title>
  </head>
  <body>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

and add <text-greeting>Hello World!</text-greeting> as a child of <body>.

<!doctype html>
<html>
  <head>
    <title>My HTML Page</title>
  </head>
  <body>
    <text-greeting>Hello World!</text-greeting>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Congratulations! You've just added a custom element to your page. No Javascript required. No need to add customElements.define anywhere. It's simply not necessary. (Unless you actually want to write a bona fide web component.) You can style your custom element with CSS, query it and manipulate it with vanilla Javascript, and use it in basically any web framework.

Er, So What the Heck is Special About DIV Then?

The only obvious difference between a custom element and a <div> tag is that custom elements are styled display: inline by default, just like <span>. So in that sense, your use of <span> and a custom element is completely interchangeable.

To make your custom element behave like a <div>, simply style it with display: block, and voila! Now your custom element is equivalent to <div>. It's that simple.

Also, unless you do use the custom elements registry and define a web component, a custom element will be of the HTMLUnknownElement class in Javascript instead of HTMLDivElement or HTMLSpanElement. That's pretty much it.

So…if all this is true and custom elements are completely valid in all modern browsers without any Javascript required…then why on earth are we stuck writing 90s-era DIVs everywhere?! It's semantically meaningless and only serves as a raw vessel for styling purposes!

The answer is…inertia and ignorance.

Everyone's already been trained to use <div> and <span> tags everywhere, so that's just what we do. It's what everybody does. Nobody ever got fired for adding a <div> tag to a React component or an ERB template or a WordPress theme. And if nobody tells us there's a better alternative, we'll never change!

Even within the past month, I've been in web dev chat rooms telling some very smart people that you can use custom elements wherever you like without Javascript, and the response is "Wait wut?? Really? Are you kidding me?!"

I don't know why all the confusion. Maybe it was terrible marketing on the part of the custom element/web component authors. Maybe it was all the deafening noise from the Big Javascript Frameworks (I'm looking at you React!) that simply don't seem to care.

But whatever the reason, I'm here to tell you that right here, right now, you can use the Custom Elements spec to your advantage and eliminate virtually all of your DIVs/SPANs in favor of expressive, semantically-useful custom elements.

Don't believe me? Well, I'll let you in on a little secret:

This website is DIV-free. 🤯

That's right, all the <div> and <span> tags are gone. (Well, 99.9% of them anyway.) Go ahead, view source. I dare ya. Look in your dev inspector. It's custom elements (and sure, plenty of regular HTML5 elements) all the way down.

Not only that, but I installed the linthtml linter as part of this site's build process so that you can't add any new DIVs/SPANs unless you explicitly bypass the lint rule. Excessive? Probably. Unwise? Maybe. But I'm here to make a point. It's time to ditch your ancient 90s-era markup and teleport into the future. Custom elements (as well as web components proper) and here and they're here to stay. We can finally have our HTML cake and eat XML-like extensibility too. Finally.

Keep reading for Part II of this story where I do a deep dive into how I converted the Bridgetown website over to using custom elements along with a few neat tips & tricks along the way for organizing and styling your custom element library.

Part II

“Make the plan, execute the plan, expect the plan to go off the rails…throw away the plan.”
Captain Cold

As we previously learned, our intrepid web developer (that would be me 😄) boldly ripped the veil off and exposed the shocking truth that the Bridgetown website was—by and large—<div> and <span> tag free. For example (simplified from the production site markup):

<layout-column class="column is-three-quarters">
  <h1>Jazz Up Your Site with Themes &amp; Plugins</h1>

  <article class="box">
    <a href="https://github.com/bridgetownrb/automations">
      <h2>automations</h2>
    </a>

    <article-content class="content">
      A collection of helpful automations that can be applied to Bridgetown websites.
    </article-content>

    <article-author class="author">
      <img src="https://avatars3.githubusercontent.com/u/63275815?v=4" alt="bridgetownrb" class="avatar" loading="lazy">
      <a href="https://github.com/bridgetownrb">bridgetownrb</a>
    </article-author>
  </article>
</layout-column>
Enter fullscreen mode Exit fullscreen mode

How was such a feat accomplished?!

Time to Execute the Plan

It’s one thing to claim you can take an existing website and convert it so it only uses semantic and custom HTML elements. It’s another thing to actually do it.

As I reviewed the usage of various div/span tags on the site, I quickly realized I would need to figure out a way to remember which tags I had already defined as I go along. For example, if I were to convert span in particular contexts to something like ui-label, I’d have to remember to use ui-label again in similar circumstances and not make up something else like widget-caption.

So one of the first things I did was create a new stylesheet called, appropriately enough, custom_elements.css. And then, one by one, as I went through every template in the website repository, I would add the elements to this one file. Here’s a snippet from the Bridgetown website:

article-author,
article-content, /* .content */
img-container,
layout-columns, /* .columns */
layout-notice,
layout-spacer,
main-content /* .content */ {
  display: block;
}

ui-label {
  display: inline;
}
Enter fullscreen mode Exit fullscreen mode

Basically this ensures a bunch of custom elements behave the same as using a div (display: block). In addition, while custom elements are display: inline by default, I wanted to enforce and remember the purpose of the ui-label element, so that’s included as well.

I also added comments to indicate that some elements have a 1:1 class name correlation with the CSS framework used by the Bridgetown site (Bulma). If I were writing a stylesheet from scratch, I could hang properties off of an element name selector itself, but on this site I have to work within an existing class-based framework.

But that’s not all. There are also some elements with 1:1 class name correlations that have their own display properties set by Bulma classes, and I didn’t want to redefine those in my own stylesheet. So I created an additional comment block at the bottom of custom_elements.css:

/*
# Class names to use for these elements:
button-group = .buttons
layout-box = .box
layout-column = .column
layout-sidebar = .column
nav-inner = .container
nav-menu = .navbar-menu
nav-section = .navbar-brand, .navbar-start, .navbar-end
ui-icon = .icon
*/
Enter fullscreen mode Exit fullscreen mode

Now anytime myself or another contributor is wondering which custom elements to use where, or which class names to use for which element, there’s an obvious reference guide available.

How Do You Verify Element Names?

This raises an interesting question: how do you make sure you haven’t added elements and forgotten to include them in the CSS comments? I encountered this conundrum right away, and the solution is simple: use regex!

<[^ !>]*?-
Enter fullscreen mode Exit fullscreen mode

In your text editor, search using this regular expression and it will find all HTML tags with at least one hyphen. Then you can quickly scan through your templates and make sure you didn’t miss anything.

It’s also a good way to double-back and refactor if you end up deciding on a new element name for a particular use case.

How Do You Come Up With Useful Element Names?

If you look at the kinds of names I arrived at during this process, many of them are derived from the parent tag they are associated with. For example, <main> is a builtin HTML5 tag, so <main-content> implies that it contains a subset of the markup within <main>. Similarly, tags like <nav-inner> and <nav-menu> are for use within <nav>, <article-author> within<article>, etc.

Other names are descriptive of the category they represent, for example all the <layout-*> and <ui-*> tags.

Something that’s important to reiterate is you shouldn’t create a new custom element lightly. Double-check there isn’t a standard HTML element already available. For instance, I don’t need to come up with custom elements to represent headers or footers because HTML already has <header> and <footer> tags.

What About Web Components?

One obvious area for future improvement is to identify which custom elements could possibly be replaced with bona fide web components—either something I write or something already available on NPM. For instance, instead of using <button> tags and then needing <ui-icon> and <ui-label> within the buttons (all using Bulma CSS classes), perhaps I could switch to using <sl-button> web components instead (provided by the Shoelace library).

As already explained in Part I, web components are by nature custom elements, but custom elements are not web components (unless you upgrade them via JavaScript). So it’s actually not a bad practice to start out with basic markup using custom elements, and then “upgrade” to using a web component if and when the need arises.

Linter Optional

Also mentioned in Part I, I installed linthtml to enforce a rule of not allowing div/span tags in the codebase. Running this linter was very helpful for finding and correcting all the violations. I’m not necessarily recommending you should take such a drastic measure in your codebase. There’s certainly nothing “wrong” with using div/span tags. I simply felt like it would be a worthwhile exercise to see if you could actually write modern HTML using only builtin semantic or custom elements for the entire website. And the answer of course is: yes you can!

Conclusion

While it was certainly a chunk of effort for no obviously noticeable gains, I remain very satisfied with the end result of this project, and it’s completely changed how I think about writing HTML and CSS for my websites and web applications. While it’s premature to say I’ll never reach for a <div> or a <span> again, what I can tell you is that it’s quickly becoming habitual not to. And I think that’s a wonderful testament to just how powerful and expressive HTML can be today.

Discussion (3)

Collapse
keisay profile image
Kevin Sengsay

Great article! I had fun comparing your website with some famous website with lot of div tags and clearly your code is more readable, it breathes better. Honestly when I started to learn how to code I did not really pay attention to best practices or writing elegant code. But as I have improved my skills since day one I do feel the need to write beautiful code. Guess I'll try that on my next web applications!

Collapse
djwebdroid profile image
DJWebdroid

Yes, great article. Question, you apply classes to the custom elements, could you not use the custom element directly in your css? Or is there something I am missing? Thanks you.

Collapse
jaredcwhite profile image
Jared White Author

In a "greenfield" project, sure, I could write custom-element-name { color: green } and so forth…but in a project with a traditional CSS framework (Bulma for example), you still need to use classes in your HTML to pick up the framework styles you want.

Forem Open with the Forem app