Re: [csswg-drafts] [css-cascade-6] `&` matching inside the `@scope`, and its interaction with `:scope` (#9740)

The CSS Working Group just discussed ``[css-cascade-6] `&` matching inside the `@scope`, and its interaction with `:scope` ``.

<details><summary>The full IRC log of that discussion</summary>
&lt;fantasai> miriam: Inside of @scope, &amp; and :scope do similar things<br>
&lt;fantasai> miriam: but slightly different<br>
&lt;fantasai> miriam: partially because of how nesting and scoping were designed<br>
&lt;fantasai> miriam: wanted &amp; to behave consistently<br>
&lt;fantasai> miriam: so it refers to the scope root<br>
&lt;fantasai> miriam: *selector*<br>
&lt;fantasai> miriam: and takes on the specificity of that selector<br>
&lt;fantasai> miriam: :scope is a bit different<br>
&lt;fantasai> miriam: it has specificity of a pseudo-class, and matches exactly one element -- the root element of the scope<br>
&lt;fantasai> miriam: it's not a stand-in for the selector, it targets a specific element<br>
&lt;fantasai> miriam: question was if those should be more the same in some way<br>
&lt;fantasai> kizu: I proposed two options, I think 2nd is more clear<br>
&lt;fantasai> kizu: If you're inside a scope, and you only mention &amp;, then you are mentioning that specific root node, same as scope<br>
&lt;fantasai> kizu: but it might be weird if you have two &amp;<br>
&lt;fantasai> miriam: You wouldn't be able to do &amp; + &amp;<br>
&lt;fantasai> miriam: My tendency here is to keep it how it is, because I think all of the solutions are potentially confusing<br>
&lt;fantasai> miriam: and at least this one is consistent with how &amp; works in other places<br>
&lt;fantasai> miriam: but open to discussion<br>
&lt;emilio> q+<br>
&lt;fantasai> astearns: Roman, your main concern is author mistakes?<br>
&lt;ydaniv> I think +1 to kizu's choice<br>
&lt;fantasai> kizu: yeah<br>
&lt;fantasai> kizu: Also, if it's an &amp;, is it equal to &amp; in scope?<br>
&lt;fantasai> kizu: otherwise where does the implicit as :scope go?<br>
&lt;fantasai> kizu: In my tests, this &amp;, matched something outside of the scope<br>
&lt;fantasai> kizu: so you could target context outside the scope, and it could match an element inside the scope<br>
&lt;fantasai> kizu: it's a weird interaction<br>
&lt;fantasai> kizu: is it the same as &amp;, where it still matches scope root; or if not, should it match anything outside the scope?<br>
&lt;fantasai> miriam: It's a scoped selector, so matching anything outside would be a bug<br>
&lt;kizu> `&amp; h1`<br>
&lt;fantasai> miriam: it's not exactly scope ... matching anything that that selector matches as long as it is in the scope<br>
&lt;fantasai> miriam: So scope doesn't necessarily end where it starts<br>
&lt;fantasai> kizu: You can inside a scope you can make adjustment on element outside the scope<br>
&lt;fantasai> kizu: because you only check whether the subject of the selector is contained by the scope, not other elements in the selector<br>
&lt;fantasai> kizu: If we do inside a scope ...<br>
&lt;kizu> `@scope … { &amp;:hover { … } }`<br>
&lt;fantasai> kizu: Do we match only on :hover of the :scope, or will it match something outside the scope?<br>
&lt;fantasai> kizu: You could argue both ways, and Safari and chrome differ last time I tested<br>
&lt;kizu> `@scope … { &amp;:hover :scope { … } }`<br>
&lt;fantasai> emilio: :hover case shouldn't match anything outside the scope<br>
&lt;fantasai> kizu: here's a better example<br>
&lt;fantasai> miriam: is that confusing though? We've said explicitly where the scope is<br>
&lt;fantasai> kizu: There was a difference in implementation, so we should clarify either way<br>
&lt;fantasai> miriam: More confusing if you did &amp;:hover p<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> emilio: Can you remind me what the implicit selector meant?<br>
&lt;emilio> `&amp; > foo`, `> foo`?<br>
&lt;matthieud> @scope … { &amp;:hover :scope { … } } Can this actually match something ?<br>
&lt;fantasai> emilio: If you don't write an ampersand, it's :scope right?<br>
&lt;fantasai> miriam: yes<br>
&lt;fantasai> emilio: I think that difference is very confusing.<br>
&lt;fantasai> emilio: To me it would make sense if &amp; was always :scope<br>
&lt;fantasai> emilio: Not sure how others feel about that<br>
&lt;fantasai> kizu: Currently everyone is doing the same, so could close it no change, because at least it's interoperable right now<br>
&lt;fantasai> miriam: Emilio, would you expect them to have the same specificity? or different specificity but matching the same element<br>
&lt;fantasai> emilio: What do we do for nesting, we add the specificity, right?<br>
&lt;fantasai> emilio: So I would expect specificity here to work the same<br>
&lt;fantasai> emilio:  ... it's not great<br>
&lt;astearns> ack ydaniv<br>
&lt;fantasai> ydaniv: In terms of specificity, I think the fact that they do match, to me it's intuitive<br>
&lt;fantasai> ydaniv: Authors still see it as appending stuff<br>
&lt;fantasai> ydaniv: Emilio's example, that the have the same specificity<br>
&lt;fantasai> astearns: You don't see an issue in changing the &amp; specificity based on location?<br>
&lt;fantasai> ydaniv: If it's the root of the scope, and &amp; matches an implicit :scope selector ...<br>
&lt;fantasai> ydaniv: so question is whether the scope-start selector or implicitly just :scope?<br>
&lt;fantasai> emilio: I think there are 3 possible answers. We have 3 related selectors<br>
&lt;fantasai> emilio: There's &lt;scope-start>, i.e. the selector in the @scope () prelude<br>
&lt;fantasai> emilio: The there's :scope<br>
&lt;fantasai> emilio: And then there's :where(:scope)<br>
&lt;fantasai> emilio: Right now we're using all three of them<br>
&lt;fantasai> emilio: &amp; means the first<br>
&lt;fantasai> emilio: implicit selector means :where(:scope), right?<br>
&lt;fantasai> emilio: and then you can type :scope<br>
&lt;fantasai> emilio: It's all fairly confusing to me, that they do different things<br>
&lt;fantasai> emilio: than a nested selector<br>
&lt;fantasai> emilio: That implicit nested selectors behave differently than explicit nested selectors<br>
&lt;fantasai> &lt;fantasai> +1<br>
&lt;fantasai> miriam: Part of what creates that is that :scope has specificity and &amp; has specificity and they're different<br>
&lt;fantasai> miriam: so what should implicit do?<br>
&lt;fantasai> emilio: Couldn't we define &amp; to be :where(:scope), so at least it's consistent with implicit just as in nested?<br>
&lt;fantasai> miriam: The @scope rule on its own doesn't add specificity, so we could say reasonably, the &amp; representing the parent is zero since scope rule has zero specificity<br>
&lt;fantasai> emilio: That also explains nested selectors within @scope<br>
&lt;romain> q+<br>
&lt;fantasai> emilio: @scope { foo ... } that's same as @scope { :where(:scope) foo ... } right?<br>
&lt;fantasai> miriam: That brings us back to Roman's question of whether &amp; matches the scope root, or other elements that happen to match the same selector<br>
&lt;fantasai> emilio: Make it match the scope root<br>
&lt;kizu> > The &amp; selector is defined to represent the selector representing the scoping root (the &lt;scope-start> selector), or else :scope if no selector was specified.<br>
&lt;fantasai> miriam: that would be different from other places where we represent the enclosing selector<br>
&lt;fantasai> kizu: &amp; is designed to represent the selector of the scoping root, not the scoping root<br>
&lt;fantasai> kizu: Right now they could consider both the specificity of the scoping root *and* ???<br>
&lt;fantasai> kizu: So maybe we adjust to make it represent the scoping root<br>
&lt;fantasai> miriam: So proposal is to make &amp; represent :where(:scope)<br>
&lt;fantasai> kizu: Yes. But we will need to see if it will not break something.<br>
&lt;fantasai> emilio: It's behavior change either way<br>
&lt;fantasai> miriam: I'm ok with this change<br>
&lt;fantasai> kizu: I think it would be better<br>
&lt;astearns> ack fantasai<br>
&lt;astearns> q+<br>
&lt;emilio> ack fantasai<br>
&lt;astearns> ack romain<br>
&lt;fantasai> romain: I'm unsure if removing specificity here is the right call<br>
&lt;fantasai> romain: I think authors will expect the specificity, and it will be weird to remove it<br>
&lt;emilio> q+<br>
&lt;fantasai> romain: Adding selectors makes things more specific. Not adding specificity when using &amp; feels weird to me.<br>
&lt;fantasai> miriam: Even though scope root selector adds no specificity by default?<br>
&lt;fantasai> romain: but :scope has specificity, it's only wrapping in an at-rule that doesn't have specificity<br>
&lt;fantasai> miriam: Are you saying have it be the same as :scope?<br>
&lt;fantasai> romain: Maybe.<br>
&lt;fantasai> romain: Writing a selector and having it produce zero specificity feels weird to me.<br>
&lt;fantasai> romain: I don't think there's any other selector that behaves like that<br>
&lt;fantasai> astearns: I'm also a little concerned about difference of &amp; in @scope vs nesting<br>
&lt;fantasai> astearns: but also, is there a way to get the specificity of the @scope prelude, if we make this change?<br>
&lt;fantasai> miriam: No<br>
&lt;fantasai> matthieud: No, and I think that was the reason for this. It allows authors to opt into this specificity<br>
&lt;astearns> ack astearns<br>
&lt;fantasai> miriam: The way we specced it is the most powerful, because you get all the options. But also the most confusing, because you have all the options.<br>
&lt;fantasai> astearns: Do we believe authors will no longer need that option?<br>
&lt;emilio> fantasai: I think the reason it's confusing is because the way specificity is introduced here is implicit in the syntax in a non-obvious way<br>
&lt;emilio> ... if we were clearer about what specificity different things have it'd be less confusing<br>
&lt;kizu> This is what we have right now:<br>
&lt;kizu> - `@scope (article) { h1 {} }` — 0.0.1<br>
&lt;kizu> - `@scope (article) { &amp; h1 {} }`— 0.0.2<br>
&lt;kizu> - `@scope (article) { :scope h1 {} }` — 0.1.1<br>
&lt;emilio> fantasai: I think the problem is that it is so implicit<br>
&lt;fantasai> miriam: It's implicit, but it's also following existing rules.<br>
&lt;fantasai> miriam: The implicit one is the only new one<br>
&lt;astearns> :scope-selector<br>
&lt;fantasai> astearns: if we make this change, and then we do find that ppl need access to the prelude selector's specificity, what are our options?<br>
&lt;fantasai> astearns: do we need a new pseudo, that targets the scope selector?<br>
&lt;fantasai> kizu: Also some proposal to have a scope-end selector, so adding scope-start wouldn't be unreasonable<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> emilio: I understand why we have the 3 options. But thing that feels really odd is that even if you explicitly nest something, e.g. using child combinator, it doesn't behave the same as &amp; in child<br>
&lt;fantasai> emilio: it differs from nesting in a really subtle way<br>
&lt;fantasai> emilio: can do anything, but feels odd to me ... I understand why we don't want @scope { } to increase specificyt<br>
&lt;fantasai> emilio: adding specificity of :scope, that would be a problem<br>
&lt;fantasai> emilio: but it's not great!<br>
&lt;fantasai> emilio: I'm ok even with no change, but it's very weird behavior<br>
&lt;fantasai> emilio: explaining it to ppl is going to be very "fun"<br>
&lt;fantasai> miriam: Other part<br>
&lt;fantasai> miriam: Does &amp; match things other than scope root?<br>
&lt;emilio> q+<br>
&lt;astearns> ack fantasai<br>
&lt;emilio> fantasai: I think there's 3 ways that could go<br>
&lt;emilio> ... we could say &amp; matches things other than the scope root anywhere<br>
&lt;emilio> ... that it only matches the scope root<br>
&lt;emilio> ... or it matches things that match the scope root but only inside the scope<br>
&lt;emilio> ... If yo do `foo bar` inside `@scope` can foo match inside the scope?<br>
&lt;kizu> Related post: https://blog.kizu.dev/scope-selector-nuance/ :)<br>
&lt;emilio> miriam: no because of the implicit scope selector, so only if you add `&amp;` or `:scope` somewhere<br>
&lt;fantasai> emilio: That implicit nesting means that ..<br>
&lt;emilio> `@scope (..) { foo bar { ... } }` is basically `:where(:scope) foo bar { ... }`<br>
&lt;fantasai> :where(:scope) foo bar, foo:where(:scope) bar, foo :where(:scope) bar  ?<br>
&lt;fantasai> miriam: No, it has the implicit `:where(:scope)` at the front<br>
&lt;fantasai> miriam: unless you put the &amp; somewhere else, so same as scope<br>
&lt;fantasai> fantasai: So &amp; represents the scope-start selector + :where(:scope) ?<br>
&lt;fantasai> kizu: that's the question<br>
&lt;emilio> emilio: no, only scope-start<br>
&lt;emilio> fantasai: what miriam said is that if it doesn't represent `:scope` it can expand the scope<br>
&lt;kizu> `&amp;:hover :scope`<br>
&lt;emilio> miriam: it can match outside of the scope<br>
&lt;emilio> emilio: because `&amp;` prevents the implicit `:where(:scope)` from getting inserted<br>
&lt;astearns> q?<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> astearns: Should we go back to Miriam's idea of choosing what &amp; matches, before we decide on specificity?<br>
&lt;fantasai> astearns: I kinda like the idea that it can only match the scope root.<br>
&lt;emilio> q+<br>
&lt;fantasai> astearns: idea of matching other things inside the scope root seems weird to me<br>
&lt;fantasai> emilio: I think it's fine, but pretty tied to the specicificity question<br>
&lt;fantasai> emilio: would be nice not to define it as something magic<br>
&lt;fantasai> emilio: we've explained it as ??<br>
&lt;fantasai> emilio: be nice to say it's just :where(:scope) or :scope, or  ...<br>
&lt;matthieud> s/??/:is(...)<br>
&lt;fantasai> emilio: Another option would be &amp; doesn't avoid injecting that implicit scope selector, that might be weird in other ways<br>
&lt;fantasai> emilio: and doesn't quite mean that it only matches scope root, just means selector can't escape the scope root<br>
&lt;fantasai> emilio: which btw, is there a way of explicitly escaping the scope root?<br>
&lt;fantasai> emilio: other than :scope(:not):scope<br>
&lt;romain> &lt;div class="row"><br>
&lt;romain>   ...<br>
&lt;romain>      &lt;div class="row"><br>
&lt;romain> @scope (.row) {<br>
&lt;romain>   &amp; :scope { ... }<br>
&lt;romain> }<br>
&lt;romain> Is this the question at hand?<br>
&lt;romain> That &amp; is ".row" and therefor this selector can match?<br>
&lt;emilio> s/:scope(:not):scope/:where(:scope, :not(:scope))` (or so)<br>
&lt;emilio> s/:scope(:not):scope/:where(:scope, :not(:scope))/ (or so)<br>
&lt;fantasai> astearns: what should we do?<br>
&lt;matthieud> (The subject can never escape the scope though, whatever the selector of the scoped rule be)<br>
&lt;fantasai> miriam: maybe write out all the options and take it back to the issue?<br>
&lt;fantasai> astearns: Questions we have are 1. What exactly does the &amp; represent. Seems like we have 4 options.<br>
&lt;fantasai> astearns: and then 2. What specificity is added when you use an &amp;<br>
&lt;fantasai> astearns: Do we have just 2 options here?<br>
&lt;fantasai> miriam: Emilio wanted it to match answer to first question<br>
&lt;fantasai> astearns: Depending on answer to first question, specificity of &amp; inside @scope may or may not match how it works in nesting<br>
&lt;fantasai> miriam: since defining as specificity of parent selector, there's at least a match there<br>
&lt;fantasai> emilio: Third question, should an &amp; keep preventing the inclusion of the implicit :scope at the start of the selector<br>
&lt;fantasai> astearns: which also may be informed by what we have it represent<br>
&lt;fantasai> emilio: Right. if we choose that it is :scope or :where(:scope)<br>
&lt;fantasai> matthieud: Different behavior for nesting and @scope<br>
&lt;fantasai> emilio: I think my preference would be to define the parent selector as :where(scope)<br>
&lt;fantasai> emilio: i.e. make &amp; as :where(:scope)<br>
&lt;fantasai> emilio: then it's like nesting, you always unconditionally add it unless it's already existing somewhere else<br>
&lt;fantasai> emilio: It does lose the scope-start specificty, but I think that's OK<br>
&lt;fantasai> emilio: because that's the behavior you get without &amp;<br>
&lt;kizu> +1 to make it  :where(:scope)<br>
&lt;fantasai> miriam: [missed]<br>
&lt;fantasai> emilio: I think that would be the more consistent solution<br>
&lt;fantasai> emilio: to say that &amp; is :where(:scope)<br>
&lt;fantasai> astearns: My concern with merely doing that is that this issue.. nothing gets done<br>
&lt;fantasai> astearns: We could resolve that &amp; matches :where(:scope) and then allow the "being wrong on the internet" effect to elicit responses?<br>
&lt;fantasai> miriam: I think it already worked to list out the options, and here we are back to take a resolution<br>
&lt;fantasai> astearns: Anyone with concerns wrt &amp; behaving as :where(:scope)?<br>
&lt;fantasai> astearns: Like more time to work through examples etc.?<br>
&lt;fantasai> andruud: It remains to be seen whether we can actually do it, since @scope has shipped<br>
&lt;astearns> ack emilio<br>
&lt;fantasai> emilio: Not yet shipping in FF<br>
&lt;emilio> fantasai: I think my suggestion would be to take a tentative resolution to give a chance to people not here<br>
&lt;emilio> ... or on vacation<br>
&lt;emilio> ... gives a bit more time to people not here<br>
&lt;emilio> +1<br>
&lt;kizu> +1<br>
&lt;fantasai> astearns: Ok, let's take a resolution and say that we will revisit in a week to confirm<br>
&lt;fantasai> PROPOSED: &amp; in @scope behaves as :where(:scope). We will revisit this in 1 week's time.<br>
&lt;fantasai> fantasai: Can Miriam or someone else post a blog post or something, to see if anyone has comments?<br>
&lt;fantasai> fantasai: can be short, like 3 paragraphs + point to issue<br>
&lt;fantasai> miriam and roman are maybe going to try<br>
</details>


-- 
GitHub Notification of comment by css-meeting-bot
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/9740#issuecomment-3028423633 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Wednesday, 2 July 2025 16:03:13 UTC