Patrick O'BrienTalksProjectsBlogAbout

Making the Case for Custom Elements

If you're a developer with exposure to the front-end world, I'm sure you've found yourself scratching your head at how to get a new project off the ground with the "latest and greatest." When that target is moving at a hundred miles a minute, it should come as no real surprise that developers are upset at the current state of the JavaScript ecosystem.
So, obviously the real solution is to throw something else at you to learn. But no, don't worry, it's not another framework. It's not even a library like jQuery (or God forbid built on top of jQuery). It's something that the large majority of modern browsers support right out of the box (looking at you, Microsoft). And if you didn't skip the title, you might know what I'm talking about...


Custom elements [Enter, stage right]

Back in 2015, a group of crazy Google developers went on a mission: discover how web apps of the future would be built, and bring a taste of that into the present. They introduced a library, Polymer, whose sole purpose was to give a taste of web components --or at least, what they thought they might be.
Web components? What's that?
Well, dear reader, let's go back just two more years. In 2013, React entered the JavaScript scene. This Facebook-backed library popularized component-driven development. Today, React is easily the most popular (both in utilization and in developer experience) framework out there.
So it's 2015 and you're looking at where browsers should go next. Google naturally envisioned a world where this component model could be used to the extreme: write custom HTML elements that can be used in any framework and in any browser. Pretty wild, right? Well, it's now 2018, and the major browsers (excluding Edge) support, or are within months of supporting (Firefox has it scheduled to release in FF 63), custom elements natively. Before I explain what they are in further detail, let's just look at a small example.
Note: this blog is not meant to be an exhaustive overview of custom elements and all the associated features. But, if you want more, check out some of the links at the bottom of this blog post.

Definition:

// use HTML <template> to stamp a reusable template to be used later
const template = document.createElement("template");
template.innerHTML = `
    <style>
      // new selector to point to the root of the element (in this case, foo-bar)
      :host {
        display: inline-block;
        margin: 0 auto;
        box-shadow: 0 0 10px rgba(128, 100, 38, 0.34);
        border-radius: 8px;
        // custom css property below lets consumers change the border color 
        border: 2px dashed var(--foo-bar-border, #ccc049);
      }
    </style>
    <span>Hello, world!</span>
    <slot></slot> <!-- this slot is where the consumer can "inject" innerHTML -->
`;

class FooBarElement extends HTMLElement {
  constructor() {
    super();

    // use Shadow DOM to encapsulate this element from the global context
    this.attachShadow({ mode: "open" });

    // attach the template from above to your element
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define("foo-bar", FooBarElement);

Usage:

<foo-bar style="--foo-bar-border: hotpink;"><div>Woo slots</div></foo-bar>
Woo slots

If you're on a browser that supports custom elements natively, you should see our foo-bar element rendered in its full glory. And, if not, you should just see "Woo slots" (because browsers will always render something, even if it doesn't know what you're trying to display).
So what are web components/custom elements? They're fully reusable, interoperable components that allow developers and designers to build high quality designs and behaviors without forcing consumers to use some predetermined framework. If you can use HTML (and CSS/JavaScript), you can use custom elements. And there's the value add right there. No monolithic CSS library, no marrying your tech stack to some framework that may be tossed out like yesterday's jam. Just plain old vanilla HTML, CSS, and JavaScript.

Caveats

Now, it would be disingenuous of me to leave you there and make this sound like the perfect solution to everything. Custom elements have their limitations.
For starters, if you have to support Edge (or worse, IE11), you'll need some polyfills. Thankfully, those Google developers I talked about earlier maintain a library just for that purpose: https://github.com/webcomponents/webcomponentsjs. I had some trouble getting document level custom CSS to work, but they thankfully have you covered to at least get off the ground: https://github.com/webcomponents/shadycss#about-customstyleinterface.
Next, you might have noticed that our element above lacked any real functionality; it's basically just some over complicated styling (and a slot!). Adding properties and attributes is supported, but there's a lot of boilerplate code. If you're at all interested in looking for some help on this front, I've had some recent success with a library being developed by Google as a replacement to Polymer, lit-html. It's lightweight and gets the job done, and comes with a complementary library called lit-element to make developing custom elements a piece of cake. However, for you brave souls who want to cut out libraries entirely, you can definitely add property observers, eventing, etc. in vanilla JavaScript. A guide on that is just a bit out of scope of this blog, though.
Finally, theming is hard. If you want to make an entire suite of UX components on this technology, you definitely can (and that is something that I am actively working on). However, it's worth noting that Custom Elements v1 (which is what is available as of this blog post) has shipped with some very real limitations with regards to styling your components. Custom CSS properties are great, but being able to override the background color, the font color, the hover state color, the active state color, etc. can be quite maddening to support. However, never fear, there are two enhancements that will probably see the light of day in one form or another: ::part and ::theme. Check out Monica Dinculescu's blog post on this for a great overview. It might be a little bit dated as of this post, but I still found it really helpful. Monica is a maintainer of the Polymer project and a very common voice in Google conferences in the web component space. And if you're in the mood for something more technical, you can always check out the drafts here.

Extra reading

This blog was intended to be more of a teaser into the wonderful world of web components. However, if you find that you're yearning for more, check out some of the below links. They've been invaluable references for me when getting started:

REFRESH