All Browsers Get This Wrong
We found a bug in Shadow DOM that's strangely consistent across Firefox, Safari and Chrome. And this bug is strange: it's not actually a user-facing issue, but rather, something the developer tools across each browser just gets wrong. 🤯
Background
I'm a huge fan of Web Components, despite their various downsides. Part of this was my time at Google: I was paid to believe in them, and it's hard to get out of that habit.
In building my startup Gumnut Dev (we make your forms collaborative), we found something odd.
Our use-case for WCs is a bit different than normal.
We provide an element <gumnut-text>
which drops-in to replace your <input>
and <textarea>
elements—but instead of being plain old editors, this editor now connects you to other users on the same page.
See a gif:

Sounds great. This element isn't styled by default, because we're a platform—we don't have an opinion on your styles.
But of course, it's been tricky to get it right: read on!
Issue
WCs typically use Shadow DOM, which has a bit of an odd quirk.
The display: ...
CSS value of your host element (i.e., <gumnut-text>
) is used as the top-level display: ...
property of the Shadow DOM.
Who cares, you ask?
Well, we care, because if you as a user of this element were to accidentally set the <gumnut-text>
element to display: inline
... this happens:

Now, who is going to do this, you ask? Well, we're not sure, but one thing you learn shipping what is fundamentally an API to your users: you want to prevent them from "holding it wrong", because if they can, they will. 🫠
My Learning
I always thought this ability to override values was a limitation or a 'dumb choice' of WCs. You have this element which doesn't really act independently because your end-users can muck with it.
However, I was wrong. 🥺
When you style your Shadow DOM, you create style which looks like this and can reference the special :host
selector.
This selector refers to "the container", so in our case, it styles <gumnut-text
.
Something like this:
:host {
display: inline-block;
}
What I didn't know is that if you set !important
on this rule, nothing can overwrite it.
This :host
declaration always wins, even over 'external' style, the style="..."
attribute, even if that value is also set to !important
.
And this is fundamentally counter-intuitive to my view of CSS priority.
So let's imagine I have an element like this (SD isn't defined like this, but let's pretend):
<gumnut-text style="display: inline !important">
::shadow-root
<style>
:host {
display: inline-block !important;
}
</style>
…your resolved display
value will be inline-block
.
Huzzah!
And of course, this applies to all properties; not just display
: you could control your element's border
, or padding
, or transform
.
Where Browsers Are Wrong
So the key of this post is that my uneducated view is actually not dissimilar to literally every browser's view, as they all have a bug in explaining what happens here. And this is problematic, because it masks the behavior—it's not discoverable!
Let's look at what Chrome says:

Firefox and Safari are similar. They all know the final resolved value, but can't explain how they got there.
Why Do I Care?
Well, you care because this greatly expands your ability as an API author to ship components that you control. WCs are a perfect fit for shippable components, because you don't have to rely on the user also using React, or Vue, or whatever framework of the week… they can just work regardless of your target.
And even though we at Gumnut Dev also ship React components wrapping up our WCs, those components still expose styling/etc attributes that apply to the internal <gumnut-text>
.
So without this control, it would be just as easy to shoot yourself in the foot and "hold it wrong", even though users of React ostensibly have a layer of abstraction in the way.
Thanks for reading! If you're interested in drop-in collaboration (no more overwriting each other's work), audit logs, history through character-level attribution—and having your forms work like they're from the future, getting out of the way of your actual business—do say hello. ♥️
Attributions
And finally, thanks to bram.us for clarifying the spec on how this should work. Cheers!