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.
Top comments (16)
Regarding the customized built-in elements: I know that there's a way more promising solution supported by WebKit team. The Custom Attributes. However, there isn't a lot of hype on it for some reason even if it looks as exciting as Custom Elements. Do you know something about this proposal and why it doesn't go forward (or maybe it does but very silently)?
Thanks for the feedback. Regarding the Custom Attributes, the only discussion I remember is this issue and also this shim by Matthew Phillips.
I don't have a strong opinion on this yet, but probably such a spec could take meta programming over DOM to the next level (if done right).
Also, I'm not sure how much performant would it be.
I have seen one more discussion which is still opened now. However, the latest message is posted over the year ago. Don't want to believe, but it could be an abandoned proposal. Even if it looks as a great replacement for customized built-in elements. More powerful, IMO, because mixins are usually have more opportunities than inheritance.
That's a cool idea and I agree it'll be very useful. But it won't cover all the cases or purposes of extending native built-ins.
Hmm, could you name a case? I'm trying to imagine but nothing comes.
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.
No, but we do want 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13... Which won't happen soon enough if they lag too much.
Safari hasn't been keeping up in general for a while now. Wanna talk shade, did you miss people starting to call it the new IE? Ouch.
There could be good reasons, like you said. I'm assuming there are--there's no reason to think otherwise. For example, a couple of those missing features are actually brand spanking new and need more vetting.
Also, I don't think there's any risk of Web Components v2. The whole point of v1 was Safari agreed. Apple had lots of good feedback on Web Components. And they're just as capable of proposing specs as Google. I definitely hope they do if they're not happy with what Google's bringing to the table! They've contributed a lot of good over the years.
First of all, thanks for your opinion. I didn't mean to be provocative here (at least, not that much as the impression makes it clear now).
Due to the objective reasons, it sometimes takes a while for Google Chrome team to convince Apple WebKit team on why they think the users need a certain low-level API. If I remember correctly, this has previously happened to Service Workers, and few smaller web APIs too.
I'm not blaming Safari here, and to be honest Firefox has had a lot of smaller issues, and missing features in particular, once they rolled 63. And I have also described the reasons for them being opposite to at least two of the mentioned issues.
In fact, the point here is both about how much the consensus means and why I'm not that excited about "Chrome-only" features shipped recently. I will consider updating the post accordingly.
Don't forget about form participation and ARIA stopping at Shadow DOM boundaries.
Or that the inability to extend native built-ins sabotages accessibility and progressive enhancement.
Or that the ability to implement components with more complex semantics like lists or tables is neutered.
Or that we really need declarative Shadow DOM.
Also template instantiation or something similar.
Agreed, CSS Named Parts are critical.
Makes me feel greedy, because Web Components are really exciting. But they need a lot more standardized to support them.
I agree that I have left certain topics out of scope of this blog post (both intentionally and not). I'm going to continue writing on this topic further, from a slightly different angle.
In the next blog post, I'm going to cover form participation API and password managers support among other things. ARIA is quite a good topic as well, thanks for pointing out.
:host-context
Is uniquely valuable in allowing a web-component design to remain encapsulated. This begins to show up more visibly as one really builds an application model and framework using components; as one might classically have done in MVC etc APP frameworks that pre-date the web. (And I worked on many, including, ironically, those that Apple became famous for in the late 80's and 1990's).
Whatever the technical or non-technical reasons, lacking it, one has to effectively mine the transitive closure of all web-components being utilized and pull out an equivalent "
:host-context
" and write it into a global-style sheet. Which defeats the purpose of having#shadow-root
support styles for encapsulation; and further pushes the compiler-linker style work-effort demands of webpack transpiling of monolithic web-properties.As in:
Allows the entirety of
<sh-main>
to be defined in a self contained mjs package, etc.Without it, one (or a compile-link webpack toolset) has to add a global style for every web-component using this technique:
Pushing all the heavy lifting into the transpilers webpack compiler-linker side of things is retro web behavior and not forward looking to a just-in-time dynamic compositional componentized architecture.
I.e., a class-like path selector model is unable scale without
:host-context
once one uses this encapsulation approach to build things like class AppModels for cmd-buttons on menu-bars etc to simplify enable/disable/visibility designs. A classic approach from MVC frameworks starting in the late 1980's and after.What may not be obvious, is that with the above type of technique of using that "sa" attribute as a path list (similar to using "class"), one can completely manage SPA view layering, modal-overlays, and cmd-btns trivially by merely listing them in three distinct body attributes. One for view-paths, one for modal-overlays, and one for enabled/disabled but visible icon/cmds.
As spec-ed
:host-context
isn't super useful anyway as you can only select "some ancestor" rather than "some parent" or even more complex relations. Maybe once:has
lands in browsers we'll be able to do:host-context(some-parent-element-type:has(> :host))
in which case I'd be more likely to care about the feature again.For leaking styles in, in a lot of cases
::part
is still too cumbersome if you want to expose many elements (although it's still great as custom-pseudo-elements). In a lot of cases I'd just like to defer styling of elements to whatever the outer tree is styling them as. Perhaps with something like:My gut feeling is that overusing
::part
could be a smell of incorrect API design of the component (so that it could be split further into a number smaller components, etc).At Vaadin, we have the workaround for writing CSS using
[part]
attribute, and then injecting those<style>
tags into shadow trees. That's convenient in most of cases, but still not ideal, and developers need some time to learn that model.Very well explained
dev.to/richharris/why-i-don-t-use-...
also explains other issues related to webcomponents.
Thanks, I have seen that post. Personally, I focus on different kind of issues.
@richharris has his own vision about how the specifications are designed and what things are missing according to him as a framework author.
On the other hand, as a component library developer, I mostly care about the issues and nitty-gritty details with the actual implementation (some of those, like Selection API, focus etc, I have faced myself or learned from my team's experience).
I respect the input by Rich, but personally I don't see any "action point" for the community in his blog post (except for "beware of using Web Components").