Scoped CSS Styles with Declarative Shadow DOM

Need to scope some CSS to a specific element in an SSR-friendly way? You can do it with web standards, no framework needed!

Codepen showing Declarative Shadow DOM

I've always thought of the Shadow DOM as something to use for custom elements only... but it can be useful for non-custom elements as well!

I was reading this excellent post by Rob Eisenberg and came across the part about composing shadow DOM and realized that you can use Declarative Shadow DOM to scope CSS to a particular element without any JS. And without any framework/dependencies. 💪

<p>
  <template shadowrootmode="open">
    <style>
      :host {
        color: red;
        font-size: 1.5rem;
      }
    </style>
    <slot></slot>
  </template>
  This paragraph is scoped
</p>

You can see a live example at this Codepen.

One thing to note is that as of July 2023 Firefox has not implemented Declarative Shadow DOM yet (see caniuse), but a polyfill is possible. Firefox does currently support the imperative attachShadow API.

Imperative Shadow DOM

Alternately, you can use the imperative attachShadow JS API to accomplish the same thing. It is supported in all modern browsers, however it's more verbose, requires JS, and is not as SSR-friendly as Declarative Shadow DOM. For an example of using attachShadow check out this codepen.

The imperative Shadow DOM works well when writing custom elements (like side-drawer) and declarative Shadow DOM works well when styling a non-custom element (like <p> or <button>).

Hopefully you found this post helpful, if you have any questions you can find me on Twitter.

How to Write TypeScript Interfaces in JSDoc Comments
Don't use TypeScript enum