Understanding the shadow dom

· 4 minutes read · 657 words

For the decades programming have existed there have been abstractions over abstractions which to many appear only as simplification of process and better DX. Yes that is the ultimate goal but it gets there by taking some tunnels.

Shadow DOM banner

Modularity is confirmed an attribute of well written programs and one of the goals for modularity is encapsulation. This is what the shadow DOM offers.

If you’ve been developing long enough you’ve probably heard this so many times by now:

Do not mess with the global scope

Although it may seem totally unrelated but shadow dom addresses the scope issue as well. It’s just different in that we aren’t talking about global variables with literals. So this isn’t about scoping this, self or global or whatever you way you call global scope in JavaScript.

A lot of us believe the problem with CSS is that all its classes (and ids if you use those in CSS) are global. And to solve this we’ve tried things like OOCSS, BEM, rscss and all of such solutions that require human effort to maintain a namespace out of the global scope. The problem with this so far is inexperienced devs would join larger codebases and mess it all up.

If you happen to be using Sass nesting without a style lint to prompt for errors it wouldn’t be long before you have this in your codebase:

.block{
  &--myModifier{}
  &.i-like-messy-code{}
  &-myNewModifier{}
  &__elementB{
    @media(min-width: 765px){
      .foo.bar.baz{
        &:after{
          content: 'danger!!!';
        }
      }
      &_element_of_element{
        color: red !important;
      }
    }
  }
}

I couldn’t control myself so I made that a little worse than it should look but the point is human are prone to errors and there are times single underscores would be used, single hyphens would be used when trying to follow something like BEM.

Well good news everyone, CSS-modules solves this problem and even though it may also be abused it seem like a better option over sprinkling global styles everywhere. The problem with CSS-modules is what if you aren’t building a React or Vue app? Or anything that complex at all? There should be a way to handle this in the web platform.

The shadow DOM tackles this problem exactly and more. It offers a way to encapsulate components as part of web components. Here’s an example

const parent = document.querySelector('.root');
const shadow = parent.attachShadow({mode: 'open'});
shadow.innerHTML = '<span>This should appear!</span><style>span{ color: red}</style>';

If you work with React, this is the ReactDOM.render() of shadow DOM.

Shadow DOM insertion
Shadow DOM on chrome 60

As shown in the figure above, the shadow root can take its own style that applies to that particular component only. Not messing with the rest of the DOM. Best part is the shadow node also doesn’t take the styles of whatever parent elements it has since it is an external and detached DOM from the regular DOM. To make it take its parent styles you’d have to explicitly tell it to:

shadow.applyAuthorStyles = true;

If you’ve ever looked through the DOM when using 3rd party libraries like modals, datepickers, and whatever cool stuff you use on your websites, you’d notice some massive additions into the DOM by them. These library authors are very much aware of the CSS cascading problems so they try to increase specificity of whatever they add. In most situations, direct inline elements are added but imagine there’s a library author that decides to add classes and pass a <style> tag with the DOM insertions. The library has a class .pod{ color: red !important } and in your code you’ve been trying a .pod{ color: papayawhip } that doesn’t seem to be working. This is the kind of clash that happens when you have everyone peeing in the DOM. ShadowDOM gives every library author their own bathroom.

Also, like the virtual dom in React, you can make traversals in the shadow dom and make only few direct data passing into the DOM improving performance over directly messing with the DOM.

Toggle Comments