问题
What do the CSS precendence rules say about the <style>
tag in shadow DOM?
I have an element <component class="component">
, a CSS file included in <head>
with:
component {
display: inline-block;
}
and a <style>
tag inside some shadow DOM with:
::slotted(.component) {
display: block;
}
If I understand it correctly, the first rule should have a specificity of 0.0.1
as it has one element and the second one specificity of 0.1.1
as it has one pseudo-element and one class. Therefore, the second one is more specific and should override the first one. This doesn't happen though. In the developer's console (Chrome) I see both the rules and neither of them crossed out and in the "computed styles" panel I see 'display: inline-block'.
A more detailed example as requested in the comments:
<head>
<style>
/* "other-component" related styles: */
other-component {
display: inline-block;
}
</style>
</head>
<body>
<some-component>
#shadow-root:
<style>
slot[name=some-slot]::slotted(*) {
display: block; /* Only works with !important. */
}
</style>
<slot name="some-slot"></slot>
<!-- The actual ("light-dom") content: -->
<other-component slot="some-slot"></other-component>
</some-component>
</body>
回答1:
This behaviour is defined in the CSS Scoping Module Level 1 Draft §3.3:
When comparing two declarations that have different tree contexts, then for normal rules the declaration earlier in the shadow-including tree order
[the first, global rule]
wins, and for important rules the declaration coming later in the shadow-including tree[the second, ::slotted(*) rule]
order wins.Note: This is the opposite of how scoped styles work.
In other worlds:
Styles that applied before distribution continue to apply after distribution.
回答2:
We might have the most in-depth explanation of the design at https://github.com/w3c/csswg-drafts/issues/2290#issuecomment-382465643
A few reasons went into the current design:
We purposely didn't involve specificity at all. Doing so would expose implementation details of the component, which makes code fragile - if the component is updated and changes the exact selector it uses, it might start overriding outside rules that previously won, or vice versa, and there's no good way for the component's user to understand or manipulate this.
So we have to decide in some other way. Document order (the final cascade step) doesn't really work here - it adds an unexpected dependency on exactly how you load the custom element, and might have interesting race
So we're left with Cascade Origin, or something close to it, that just unreservedly makes one or the other win. Actually injecting a new origin into the list didn't seem like a great idea; it's unclear how user vs author stylesheets should interact with this. So instead we add another cascade step for this.
And finally, we have to make a decision about which wins. We already know that whatever order we choose, !important should have the reverse order; this is how the cascade origins already work. So we have to decide whether the outer page wins by default, but the component wins in !important, or the reverse. We decided that the former made more sense; this means that the component author's normal styles are "defaults", the component user's styles (!important or not) can override that, and the component author's !important styles can be used to "lock down" styles that need to stay how they are. Going the other way around didn't seem to satisfy use-cases as well: it would mean that component users can't override styles by default; they'd have to use !important to do so, possibly interfering with their other styles; and then component authors would have no way of "locking down" styles.
来源:https://stackoverflow.com/questions/54884345/css-precendence-in-shadow-dom-style