csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

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

Open andruud opened this issue 1 year ago • 6 comments

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)

andruud avatar Jun 12 '24 07:06 andruud

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 avatar Jun 12 '24 08:06 mirisuzanne

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

andruud avatar Jun 12 '24 10:06 andruud

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 avatar Jun 12 '24 12:06 mirisuzanne

Agenda+ to discuss along with #10389 and #9621

mirisuzanne avatar Jun 13 '24 16:06 mirisuzanne

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

  • they apply to the :scope element
  • 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 :scope wrapper on scope-nested rules should not impact specificity. Which requires us to revert/clarify how that implicit :scope is 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 :scope element, without additional specificity
  • #9621 Clarify serialization of implicit :scope, so no implicit specificity is added

mirisuzanne avatar Jun 25 '24 18:06 mirisuzanne

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.

css-meeting-bot avatar Jul 03 '24 23:07 css-meeting-bot