Nathan Knowler

Extensible Web Components

Permalink

The promise of encapsulated styling is usually what attracts people to Web Components and it’s also where a lot of people get their start actually writing them. For many, that’s what Web Components are: bits of Shadow DOM with isolated styling. It certainly is one of the most magical aspects of them—especially for anyone who’s ever struggled with CSS. Writing CSS freely without a thought of specificity, existing styles to override, or just not breaking something else on the page seems liberating.

It doesn’t take long for the euphoria to wear off. Often it’s replaced with confusion when people begin to introduce features like slots into their components. “What do you mean, the Light DOM styles take precedence over the Shadow DOM styles? I thought it was supposed to be the opposite?” It’s also scandalizing for some to discover that, yes, CSS properties do inherit through a shadow root boundary and this is true for (unregistered) custom properties whose default behaviour is to inherit their value. This only ends up coming across that the Shadow DOM isn’t real encapsulation. Something with our mental model of Web Components isn’t quite right.

Starting with styling the Shadow DOM is a misstep

As I was watching and participating in numerous conversations about Web Components take place over the last few days, when Robin Rendle pointed out that “Folks start with styles first[…]” it made me realize the starting with styles is indicative of our misunderstanding of what Web Components are and where to start when we build them.

The focus on isolated, scoped styles creates a sense that Web Components are meant to be closed off as a default. To be frank, this is against the grain they were built. We might as drop the “Web” from Web Components if this is what we expect.

In reality, the way that features like slots work (i.e. the CSS ::slotted() selector used for styling slotted Light DOM content from the Shadow DOM), having less precedence over the styles for the actual slotted Light DOM content, and the same for CSS Parts used to expose elements from the Shadow DOM for styling outside, show us that Web Components were designed for extension.

The web is extensible

Extensibility is an underlying principle of the web. Web Components do not deviate from this principle. Features like slots and CSS parts should be embraced when using the Shadow DOM. Why? Because they empower the extension of what is already there and they allow the extension of what they introduce.

Often, the impulse I see in people (myself included) in building Web Components is to move chunks of their markup and styles to the Shadow DOM. Doing this closes off that markup and those styles from extension. This is a choice that will get us in loads of trouble. Not only is that content inaccessible without the use of JavaScript, potentially fraught with technical and accessibility bugs, but now we’ve limited its ability to extend the existing Light DOM and to be extended with CSS.

Our approach to Web Components should be one that is open by default. We should embrace extensibility in our designs and build with the rule of least power in mind. I think in practice this means adopting approaches like progressive enhancement. When this is our approach, we realize that the Light DOM is where we should start and that it serves as valuable foundation to extend.

Conclusion

Now, I hope that I haven’t come across as being dismissive of the Shadow DOM. I actually think it’s pretty great, but what I hope I’ve done, is highlight that when it’s the first and only thing we focus on for Web Components, we actually misunderstand how to use it and it’s purpose.

In the future, I’ll expand on my thoughts of how to best understand writing styles for the Shadow DOM that lead to a more extensible web. I think understanding Web Components as being extensible makes styling the Shadow DOM make a lot of sense.

Today, I encourage you to get started with Web Components. Start with the most humble and least powerful thing: a custom element—no CSS or JavaScript required (though, a different display property might be a good idea).