Light-DOM-Only Web Components are Sweet
Light-DOM-Only Web Components are Sweet ź“ė Ø
First: the Light DOM is just⦠the regular DOM.
When people talk about native Web Components, the Shadow DOM comes up a lot. I have extremely mixed feelings about the Shadow DOM. On one hand, itās a powerful scoping tool. For example, CSS applied inside the Shadow DOM doesnāt āleakā outside, meaning you can be freer with naming and not worry about selector conflicts. Also, JavaScript from the outside canāt reach in, meaning a querySelectorAll
isnāt going to select things inside the Shadow DOM. Itās a protective barrier that is unique to Web Components. No library can offer this, and thatās cool.
Thatās what the Shadow DOM is. What it isnāt is required for āHTML abstractionā, which I feel like it gets confused for. It had me confused for a while, anyway. If one of your goals for a Web Component is to hide away the HTML implementation details of a component, perhaps for ease-of-use, well, thatās great, but you can do that with the Shadow DOM or the Light DOM.
Letās get into this.
The Guts of a Custom Element Can Become Light DOM
Hereās a custom element defined as alert-machine
:
<alert-machine>
<button>I'm a button.</button>
</alert-machine>
In order for that to do anything, we need to execute some JavaScript that defines that element and its functionality:
class AlertMachine extends HTMLElement {
connectedCallback() {
this.querySelector("button").addEventListener("click", () => {
alert("Alert machine strikes again.");
});
}
}
customElements.define("alert-machine", AlertMachine);
I kinda prefer the slightly more elaborate setup with a constructor that allows for methods and such:
class AlertMachine extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.querySelector("button").addEventListener("click", this.doAlert);
}
doAlert() {
alert("Alert machine strikes again.");
}
}
customElements.define("alert-machine", AlertMachine);
Even if this is the first time youāre ever seeing Web Components code like this, you can make sense of it.
What I like about what is happening so far is that <button>
in the HTML is going to render on the page just like⦠a⦠<button>
in HTML. Itās 100% just like that:
- It renders like a
<button>
- You can select it and style it from regular CSS with
button { }
- You can
querySelector
it from regular JavaScript
Itās no different than any other HTML on the page.
But notice the JavaScript inside the Web Component is adding a little extra functionality. Thatās it. Neat.
You Can Augment or Replace the HTML with Whatever
At this point, you might think:
- OK, whatever is inside the custom element is the Light DOM. And:
- Well, thatās pretty limiting. I donāt want to have to be in charge of adding in all the needed HTML for every single component. That doesnāt bring very much value.
Thatās kinda true, but it doesnāt have to be the entire truth. You can totally wipe that away and inject your own HTML. You could keep that HTML but add more, wrapping things or whatever you need to do. Or you could just keep it as is. This is really no different than the possibilities with Shadow DOM. Do what you gotta do. You do lose the ability to use <slot />
, which is pretty unfortunate, and I wish wasnāt true, but alas.
So if your goal is to abstract away a bunch of HTML to make your component easier to use, go for it!
But you probably should get on board with delivering a good set of HTML within your custom elements right in the delivered HTML. For one, thatās the fallback when JavaScript fails to load or run, which matters. But itās not just a āfallbackā or progressive enhancement technique entirely, itās what is required for SSR (server-side rendering) which is also a performance concern and I think we all generally agree is a good idea.
While I think the possibility that not having a build process for Web Components is attractive, if what the build process buys you is good, solid, HTML, then itās probably worth it. (Iām mostly thinking of WebC (11ty/webc
) and Enhance here).
<my-component>
<div class="card">
<h2>Good, solid, renderable, accessible, non-embarassing HTML in here.</h2>
<div>
</my-component>
Take, for example, the Image Comparison Slider that Cloud Four put out as a Web Component:
<image-compare>
<img alt="Alt Text" src="path/to/image.jpg" />
<img alt="Alt text" src="path/to/image.jpg" />
</image-compare>
Just two images! Thatās a perfect fallback for this, because two images are exactly what youāre trying to show. But when the comparison slider loads, itās got all sorts of other HTML that make it do the interactive stuff. Thatās perfect.
Their slider does use the Shadow DOM, which is fine of course. And actually, they use <slot />
to put the images into place in the abstracted HTML is pretty useful. But they didnāt have to, this all could be done in the Light DOM. It would be a little extra work producing the final HTML, but a hell of a lot easier for consumers to style.
Jim Nielsen has a nice way of saying it:
Jim Nielsen (blog.jim-nielsen.com
)
This feature of web componentsĀ encourages a design of composability. Rather than an empty āshell componentā that takes data and (using JavaScript exclusively) renders the entirety of its contents, web components encourage an approach of composing core content with HTML and then wrapping it in a custom element that enhances its contents with additional functionality.
I donāt like styling the Shadow DOM
Thatās kind of the rub here, for me. The main reason Iām so hot on Light DOM is that I find the styling story of Web Components using Shadow DOM annoying.
- Styling very specific things with
::part
is a very niche styling thing and to me, not a real styling solution. - Styling by documenting somewhere that certain CSS
--custom-properties
are in use is also limited and not a real styling solution. - Styling by injecting a
<style>
tag into some template literal in the JavaScript itself feels awkward and ad hoc to me and Iām not a fan. - Styling with an adopted stylesheet means an additional web request for each component or back to the template literal thing which is either awkward or slow.
I donāt dislike that these options exist, I just donāt⦠like them. Iād rather be able to use the best styling API ever: regular CSS. There is some hope here, the idea of āopen stylable shadow rootsā (WICG/webcomponents
) might take hold.
So what are we giving up again?
If you go all-Light-DOM with a Web Component, you lose:
- Encapsulation
- Slots
You gain the ability to use regular olā CSS from the parent page to style the thing. And, perhaps, a stronger reminder that Web Components should have good default HTML.
What People Are Saying About Light DOM
They certainly resonate with Eric Meyer!
Blinded By the Light DOM (meyerweb.com
)
IĀ likeĀ the Light DOM.Ā Itās designed to work together pretty well.Ā This whole high-fantasy-flavored Shadowlands of the DOM thing just doesnāt sit right with me.
If they do for you, thatās great!Ā Rock on with your bad self.Ā I say all this mostly to set the stage for why I only recently had a breakthrough using web components, and now I quite like them.Ā But not the shadow kind.Ā Iām talking about Fully Light-DOM Components here.
Some baby bear just-right porridge from Jeremy Keith:
HTML web components (adactio.com
)
Dave talks about how web components can beĀ HTML with superpowers. I think thatās a good attitude to have. Instead of all-singing, all-dancing web components, it feels a lot more elegant to use web components to augment your existing markup with just enough extra behaviour.
Where does the shadow DOM come into all of this? It doesnāt. And thatās okay. Iām not saying it should be avoided completely, but it should be a last resort. See how far you can get with the composibility of regular HTML first.
Mayank has a pretty hardline stance, and gets into similar problems I have with styling.
Mayank (mayank.co
)
Iāve previously saidĀ āshadow DOM is not fit for production useā, a statement which attracted a surprising amount of heat. Maybe Iām asking for too much, but I would think that every respectable production-grade application has core needs ā like accessibility, form participation, and the ability to work without JavaScript.
Today though, I want to touch a little bit on theĀ stylingĀ side of things.
Jim Neilsen used Light DOM only, found it useful, and even felt weird about it (which you should not, Jim)!
Jim Neilsen (blog.jim-nielsen.com
)
Maybe I shouldnāt be using the term āweb componentā for what Iāve done here. Iām not using shadow DOM. Iām not using the templates or slots. Iām really only usingĀ custom elementsĀ to attach functionality to a specific kind of component.
But it still kinda feels like web components. All of this couldāve been accomplished with regular oleā web techniques
Adam Stoddard is into it:
No shadow DOM, no templates, just the regular old DOM, which we now get to call the much cooler sounding ālight DOMā.
Adam specifically call out how cool the connectedCallback
is. Whenever a custom element appears in the DOM it essentially auto-instantiates itself, which is an āunsung heroā of Web Components. Dave Rupertās simple and useful <fit-vids>
has no Shadow DOM in sight (davatron5000/fit-vids
), it just applies a few styles to what it finds in the Light DOM, but another reason to use it is how it automatically applies the styles when it shows up in the DOM. If you were to use the old school fitvids.js library, you would have to re-call the library if new videos were injected after it ran the first time.
Iāll end with Miriam Suzanne:
Miriam Suzanne (oddbird.net
)
Let the lightĀ DOMĀ handle content wherever possible.
How about yāall? How you feeling about the Light DOM approach?