csswg-drafts
csswg-drafts copied to clipboard
[css-cascade] @scope as a nested grouping rule and CSSNestedDeclarations
This came up when discussing #10389:
It is currently possible to place bare declarations directly in a @scope rule if it's a nested grouping rule:
div {
@scope (#foo) {
color: green;
#bar {
width: 10px;
}
z-index: 42;
}
}
I think current WPTs (which haven't yet picked up #10234, nor the @nest edit which came before it) require the above to desugar to:
div {
@scope (#foo) {
:scope { /* <== The selector and its specificity being the relevant part for this issue */
color: green;
z-index: 42;
}
#bar {
width: 10px;
}
}
}
(I think per specs that should strictly have been wrapped in a &{} actually, but I apparently forgot to raise an issue for this).
As of #10234, I believe we now intend to desugar to:
div {
@scope (#foo) {
/* CSSNestedDeclarations { */
color: green;
/* } */
#bar {
width: 10px;
}
/* CSSNestedDeclarations { */
z-index: 42;
/* } */
}
}
Where the CSSNestedDeclarations rules match whatever #foo matches (but within the scope, obviously), and with ... the same specificity as #foo? It's this specificity part I'm not sure about, as it doesn't seem consistent with how "implied stuff" in @scope is intended to work. For example, the #bar selector effectively gets an implied :where(:scope) selector prepended (#10196), which adds no specificity.
So I wonder if we should specify that CSSNestedDeclarations rules, when the appear directly beneath @scope, should act as :scope {} or :where(:scope) {} rules? (@mirisuzanne)
Keeping scope consistent, I think we would use :where(:scope) {}, so the resulting specificity is 0,0,1. The only specificity comes from the div.
@mirisuzanne But the innermost rules would get 0,0,0 if we use :where(:scope){}, not 0,0,1. The innermost rules do not "see" the outer div rule. The thing that maintains a connection between div and the innermost rules is the intermediate <scope-start> selector, which gets an implicit & prepended. Inner :scope selectors then match the scoping roots created by that <scope-start>.
oh, you're right - the result is more like :where(div #foo) rather than div :where(#foo).
Huh, we should likely discuss these two issues together in the group. I think the logic makes good sense at every point along the way, but the zero-specificity result for bare declarations is still a bit surprising. Maybe a fine and reasonable result? But something we should clearly state, and demonstrate in examples.
Agenda+ to discuss along with #10389 and #9621
The proposal here is to treat directly-nested declarations in a scope rule such that:
- they apply to the
:scopeelement - no specificity is added
That is, as though the declarations are wrapped in :where(:scope) { /* declarations */ }
- This comes out of our previous resolution (https://github.com/w3c/csswg-drafts/issues/10196#issuecomment-2161119978) that an implicitly added
:scopewrapper on scope-nested rules should not impact specificity. Which requires us to revert/clarify how that implicit:scopeis serialized (https://github.com/w3c/csswg-drafts/issues/9621#issuecomment-1864891688) - This would also resolve https://github.com/w3c/csswg-drafts/issues/10389, since it means we are allowing bare declarations inside a scope rule.
I would like to resolve all three aspects of this at once if we can:
- #10389 Resolve to allow bare declarations in
@scope - #10431 (this issue) Bare declarations apply to the
:scopeelement, without additional specificity - #9621 Clarify serialization of implicit
:scope, so no implicit specificity is added
The CSS Working Group just discussed [css-cascade] @scope as a nested grouping rule and CSSNestedDeclarations, and agreed to the following:
RESOLVED: bare declarations in a scoped rule apply to the scoped root and add no specificity.
The full IRC log of that discussion
<khush> andruud: when a @scope rule is a nested grouping rule we allow bare declarations within its body. Now such declarations should be wrapped in a nested css rule.<khush> the css nested declarations rule is generally defined to match whatever the parent selector matches
<khush> with same specificity behaviour
<khush> butt this may not make sense for at-scope since it's not how implicit selector stuff works for scope in general
<khush> so we should have nested css declation rule inside nested matches the scoping root with no specificity
<khush> astearns: this matches miriam's proposal?
<matthieud> s/inside nested/inside scope
<khush> miriam: yes. scopes don't add specificicity which matches this. bare declarations in the scope matches the scope root.
<khush> andruud: when it's not nested, it's not allowed. separate open issue
<khush> miriam: this matches previous stamenents. +1
<khush> astearns: from glancing at your last comment, there are other things to resolve on this?
<khush> miriam: they'll fall out so let's be clear.
<khush> one resolution was to serialize the implicit scope but that adds specificity. so we need to change that to work the same way, implicit scope is wrapped in a ware pseudo class which hides the specificity of selectors inside it
<khush> also allow declarations directly inside scope
<khush> astearns: so nested related issues
<khush> miriam: doing all will get consistent behaviour
<khush> astearns: sounds like if we're serializing the bare decls in a scope such that they are wrapped in a ware pseudo?
<khush> andruud: if the ware pseudo is implicitly added it should not be serialized
<khush> astearns: no problem then
<khush> astearns: any other comments?
<matthieud> s/ware/:where
<astearns> :where
<miriam> :where(:scope)
<khush> matthieud: it's not directly related to this issue but in general what's the meaning of something nested in a rule. It's just gonna have specificity of pseudo-class of 1. We need to add specificty of each layer or cascading won'y work
<khush> miriam: it puts the implicit & in the scoped rule. so the scoped root is a nested selector of the parent
<khush> andruud: this is an existing issue?
<khush> matthieud: resolution won't change the behaviour but wanted to bring this up.
<khush> astearns: so should we resolve on what we were first discussing and then go through each implication in turn?
<khush> miriam: not sure how well the resolution is on the issue
<khush> miriam: for this one, where declarations in scope should be treated as a css nested declaration?
<khush> andruud: the where declarations apply to the scoping root with no extra specificity
<khush> astearns: it's not where and bare declarations in the above
<khush> matthieud: do we want specifity of the start or the complete selector which could be more complext
<khush> miriam: scoped root doesn't add specificty, only when you use & or :scope. If you're adding a bare declaration then no specificty added
<khush> astearns: proposed resiltion, bare declarations in a scoped rule apply to the scoped root and add no specificty.
<khush> astearns: objections?
<khush> RESOLVED: bare declarations in a scoped rule apply to the scoped root and add no specificity.
<khush> astearns: other related issues?
<khush> miriam: clarify serialization. we can't serialize it with :scope. Won't do what we just resolved.