w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 664 forks source link

[css-cascade] @scope as a nested grouping rule and CSSNestedDeclarations #10431

Open andruud opened 5 months ago

andruud commented 5 months ago

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)

mirisuzanne commented 5 months ago

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.

andruud commented 5 months ago

@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>.

mirisuzanne commented 5 months ago

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.

mirisuzanne commented 5 months ago

Agenda+ to discuss along with #10389 and #9621

mirisuzanne commented 4 months ago

The proposal here is to treat directly-nested declarations in a scope rule such that:

That is, as though the declarations are wrapped in :where(:scope) { /* declarations */ }


I would like to resolve all three aspects of this at once if we can:

css-meeting-bot commented 4 months ago

The CSS Working Group just discussed [css-cascade] @scope as a nested grouping rule and CSSNestedDeclarations, and agreed to the following:

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.