After the recent public announcement of Chromium-based Edge canary builds, there has been a lot of excitement on Twitter because of the Web Components finally getting closer to land in all evergreen browsers. The Polymer community has been waiting for it so many years, and I share that feeling. Web Components are here... but I would still say it very carefully.
Let’s assume we don’t have to support IE11 or any other legacy browsers. So, evergreen browsers with native support for both Custom Elements and Shadow DOM. The good news is that we can forget about polyfills. The bad news is that “zero polyfills” doesn’t really mean “zero bugs”. Today, I would like to share my pain and talk about the problems that still exist.
The blog post is written based on 3 years of commercial experience with all the Polymer versions since 0.5 (yes, I still remember a weirdness of multiple shadow roots in the same component). During that period, I have been working both on the quite complex production application and a web component library, so my opinion is based on the actual background.
I’m writing this now also because of the upcoming “face to face” (F2F) meeting regarding the progress on Web Components implementation. Both the browser vendors and the representatives of the companies like GitHub and Salesforce are planning to attend it. The topics to be discussed include the future, which seems bright and shiny, as usual. But today I‘m going to focus more on the present, as there are still issues unlikely to be ever fixed.
The first one (you guessed) is about extending built-in HTML elements. Let’s say we want to implement our own anchor and use it for routing, which is a common practice for SPA. I was using such a web component at the times of Custom Elements V0 and it worked nicely. But Safari refused to implement “is”
attribute, as they didn’t want to change HTML parser.
So, one can not simply extend the anchor (or button, or anything else like that) in Safari. And that’s very unlikely to change, even the corresponding issue has been closed long ago. It’s not even covered by the “official” polyfill by Google. Of course, there is a community polyfill for it. But does anyone really want to use it and monkey patch the browser internals forever?
Another problem where consensus with Safari wasn’t met is related to styling Shadow DOM. Specifically, it’s about :host-context
CSS selector, designed to change a web component's styles based on its parent. This kind of inversion of control is not supported anywhere else in CSS. And Safari refused to implement :host-context
and requested to remove it from the specification. So far, it only works in Chrome. Sounds familiar, doesn’t it?
The reason why :host-context
is so important is that it’s the only way for the web component to adjust styles based on its parent. As an example, in some CSS frameworks, the <button>
placed inside disabled <fieldset>
should look disabled. We can achieve it with global CSS, but with Shadow DOM we can not. The only thing we can really do is... to change our mind.
One more thing which Shadow DOM affects is handling the focus, especially navigation order when tabbing to any focusable element, like <input>
, inside the shadow root. This again works in Chrome only, with the flag called “delegatesFocus”
passed to attachShadow()
call. One line is expected to do a lot of magic and save us a lot of time once all browsers support it. Until then, we have to use temporary workarounds.
Did I say “temporary”? At the time of writing, the state of things sounds like this: nobody has done the work to refactor and integrate the feature into the HTML specification. And the developer from Chrome, who had been assigned to work on that long ago, has since left the team. So there hasn’t been any progress for almost 3 years. I really hope to see it after F2F meeting.
Moving forward, we stumble on yet another shadow DOM related thing. So now, a few words about CSS shadow parts. They have been recently shipped in Chrome and there has been some interest from Firefox, which is migrating its internals to Custom Elements. But again, no update on WebKit tracking issue for more than a year. As a result, yet another Chrome-only feature.
Unlike the examples above, missing support for CSS shadow parts doesn’t break anything… except that it completely breaks developer experience. In the real world, style encapsulation can be an evil for the users – especially when a web component has a lot of customisable internals, it is hard to provide a flexible architecture using only custom CSS properties.
In fact, users don’t really want styles to leak out the component. But at the same time, they often want certain styles to magically leak in. Yes, this has been partially covered with the Constructible Stylesheets (have you been able to guess the only browser supporting them as of today?). But we still need CSS shadow parts to replace abandoned CSS mixins and @apply
.
So far so good, but it looks like I have to mention Safari once again – now because of a more advanced topic. If you have ever tried to use a rich text editor JS library like CKEditor, Quill, Prosemirror or even Trix (which is built with Custom Elements) inside a shadow root, you will understand me. And that’s again the issue to be fixed by the browser vendors.
Calling document.getSelection()
in Safari doesn’t return nodes from shadow trees. Chrome, in its turn, implements this method also on shadow roots, but Safari doesn’t. There has been a rough consensus at one of the previous F2F meetings and a related issue submitted to the corresponding repo. Until this is fixed, we should consider using shadow DOM carefully.
As you might have noticed, all the issues except the first one are related to Shadow DOM. Even without the ShadyDOM shim making certain monkey-patched API slow in "good old" Edge, we still have problems to keep in mind. The declarative shadow DOM proposal (needed for server side rendering) opposed by the implementers is yet another serious challenge.
I hope it is now clear why so many people aren’t that excited about Web Components specs. They do provide a missing primitives and solve a certain amount of problems – at the cost of introducing other challenges. So, blind evangelism in support of the Web Components can actually result in backfire, as someone could call them a “broken promise” even today.
Wrapping it up, I do believe that Web Components are a big thing, and will hopefully make the architecture of our web apps more flexible, and the size of our bundles smaller. They can be compared to CSS grid or ES modules, except the fact that Web Components are coupled with all the underlying parts of the web platform: HTML, CSS, JavaScript and DOM.
And finally, as already mentioned above, I really hope to update this post in the upcoming weeks and to see at least certain of those tough questions resolved. And if you, the reader, watch as many GitHub repositories and issues as I do, you probably can give me a hand with some inside. Anyways, I would be glad to hear any valuable feedback regarding this post!
UPD: Please note, I'm not blaming neither Safari nor Chrome here, and to be honest Firefox has had a number of smaller issues too, once they rolled version 63. The point here is about how much the consensus means, and how long does it take for Web Components APIs to mature and stabilise.
I think is a lot of unnecessary shade thrown towards Safari in this post. They are indeed slow to adopt new features, but in general I find their implementation quality to be better than that of Chrome in several areas.
If I had the choice between having everything implemented tomorrow, or a more sane and well refined standard, I'd choose the better standard every time. Nobody wants a Web Components v2.