Skip to content

[css-cascade-6] Specificity of Implicitly-Added :scope in Scoped Rules #10196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dshin-moz opened this issue Apr 10, 2024 · 12 comments · Fixed by #11135
Closed

[css-cascade-6] Specificity of Implicitly-Added :scope in Scoped Rules #10196

dshin-moz opened this issue Apr 10, 2024 · 12 comments · Fixed by #11135

Comments

@dshin-moz
Copy link

dshin-moz commented Apr 10, 2024

Current WPT test for scoped styles' specificity seems to assume that an unscoped rule has the same specificity as the identically written out scoped rule.
However, the spec's example seems to imply that :scope <descendant-combinator> is implicitly added, and that :scope's specificity is equal to that of other pseudo-classes (i.e. class-like +1).

WebKit and Blink are shipping the behaviour matching the WPT.

This leads to behaviours where functionally-identical selectors with explicitly-added :scope selector wins despite the ordering. In the example below, WebKit and Chrome both show .foo being purple:

<!doctype html>
<style>
.foo { color: green }
@scope (:root) {
  :scope .foo { color: purple }
  .foo { color: blue }
}
</style>
<div class="foo">Color?</div>

cc: @emilio, @mdubet, @andruud

@dshin-moz
Copy link
Author

On the other hand, a parallel could potentially be drawn to:

<!doctype html>
<style>
:root .foo {
    color: red;
}
.foo {
    color: lime;
}
</style>
<div class="foo"></div>

Where .foo is red.

@emilio emilio added the Agenda+ label Apr 10, 2024
@emilio
Copy link
Collaborator

emilio commented Apr 10, 2024

Yeah this is also important because depending on what behavior we decide, we might need to revert the resolution in #9621 (or change it so that we serialize to :where(:scope), but that's a bit weird...

I think it's very weird that:

> .foo

Has different specificity than:

:scope > .foo

in this case...

cc @mirisuzanne @tabatkins

@andruud
Copy link
Member

andruud commented Apr 10, 2024

Right, right.

Well, it looks like the current behavior is intentional at least, as @mirisuzanne and I apparently briefly discussed this June 19th, 2023 (chat history). We both concluded (from the discussion in #8500) that any implicit :scope should not contribute to specificity.

I also noted at the time:

"That means @scope (.a) { .b { ... } } and @scope (.a) { :scope .b { ... } } are different rules, and we can not serialize one as the other."

So yeah, oopsie. :-)

With both Chrome and Webkit having shipped now, a change here could be too late already. I'll try to investigate that if we indeed want to change.

@mirisuzanne
Copy link
Contributor

It was intentional, and designed to match e.g. :root. The goal is for the scope rule itself not to impact specificity, while both :scope and & selectors can reference it in different ways, and bring their own specificity implications to bear. I don't like either of the alternatives:

  • :scope has been around for some time, and it makes sense for it to have a pseudo-class specificity (since that's what it is)
  • We do not want to add an arbitrary pseudo-class specificity to all scoped selectors, and it becomes extra confusing if :scope has a specificity, but adding/removing :scope from the selector has no specificity impact.

@dshin-moz
Copy link
Author

Should :scope inserted by & + Implicit scope root add to specificity? Blink & WebKit diverge on it:

<!DOCTYPE html>
<div>
  <style>
    @scope {
      & .foo {
        color: blue;
      }   
    }   
    :scope .foo {
      color: red;
    }   
  </style>
  <div class="foo">What Color?</div>
</div>

Shows red on Chrome dev 125.0.6382.3 and blue on Safari Technology Preview 17.4.

@andruud
Copy link
Member

andruud commented Apr 19, 2024

I would expect the & to have its regular "as if :is()"-specificity behavior, which in this case means the specificity of :is(:scope).

..... which means Chrome doesn't do what I expect.

@mirisuzanne
Copy link
Contributor

I would expect the & to have its regular "as if :is()"-specificity behavior…

That's what we intended, yes. See https://www.w3.org/TR/css-cascade-6/#example-66991251 for example.

moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue May 30, 2024
`:scope` gets implicitly added if not present, without contributing
specificity (See w3c/csswg-drafts#10196).

`&` is replaced with `scope-start` selector, or `:scope` if it not
specified.

Differential Revision: https://phabricator.services.mozilla.com/D207780

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1886441
gecko-commit: fae17dbd22d2e12315bca8fc854a837fde4dcc7d
gecko-reviewers: firefox-style-system-reviewers, emilio
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue May 30, 2024
…style-system-reviewers,emilio

`:scope` gets implicitly added if not present, without contributing
specificity (See w3c/csswg-drafts#10196).

`&` is replaced with `scope-start` selector, or `:scope` if it not
specified.

Differential Revision: https://phabricator.services.mozilla.com/D207780
moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue May 30, 2024
`:scope` gets implicitly added if not present, without contributing
specificity (See w3c/csswg-drafts#10196).

`&` is replaced with `scope-start` selector, or `:scope` if it not
specified.

Differential Revision: https://phabricator.services.mozilla.com/D207780

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1886441
gecko-commit: fae17dbd22d2e12315bca8fc854a837fde4dcc7d
gecko-reviewers: firefox-style-system-reviewers, emilio
github-actions bot pushed a commit to servo/stylo that referenced this issue May 31, 2024
…style-system-reviewers,emilio

`:scope` gets implicitly added if not present, without contributing
specificity (See w3c/csswg-drafts#10196).

`&` is replaced with `scope-start` selector, or `:scope` if it not
specified.

Differential Revision: https://phabricator.services.mozilla.com/D207780
jamienicol pushed a commit to jamienicol/gecko that referenced this issue May 31, 2024
…style-system-reviewers,emilio

`:scope` gets implicitly added if not present, without contributing
specificity (See w3c/csswg-drafts#10196).

`&` is replaced with `scope-start` selector, or `:scope` if it not
specified.

Differential Revision: https://phabricator.services.mozilla.com/D207780
@astearns astearns moved this to Unsorted in CSSWG June 2024 meeting Jun 3, 2024
@astearns astearns moved this from Unsorted to Wednesday morning in CSSWG June 2024 meeting Jun 3, 2024
@astearns astearns closed this as completed by moving to Wednesday morning in CSSWG June 2024 meeting Jun 3, 2024
@emilio
Copy link
Collaborator

emilio commented Jun 3, 2024

@astearns this one was also unexpectedly closed I believe.

@astearns astearns reopened this Jun 3, 2024
@astearns astearns moved this from Wednesday morning to Wed morning in CSSWG June 2024 meeting Jun 3, 2024
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-cascade-6] Specificity of Implicitly-Added `:scope` in Scoped Rules, and agreed to the following:

  • RESOLVED: explicitly state in specs that when an ampersand doesn't actually have a parent rule to draw from then its specificity is zero
The full IRC log of that discussion <jarhar> emilio: the at scope rules insert an explicit :scope selector when you dont have it, much like nesting
<jarhar> emilio: its weird but it seems intentional as per the discussion in the issue that :scope doesnt contribute to specificity
<jarhar> emilio: its weird but it also seems like if you do the ampersand then webkit and blink have different behavior which is weird
<jarhar> emilio: i think i would expect webkits behavior there
<jarhar> emilio: thats basically a question
<jarhar> miriam: webkits behavior in which?
<jarhar> emilio: in the ampersand case.
<TabAtkins> https://github.com//issues/10196#issuecomment-2065371007
<jarhar> TabAtkins: the example with which behavior chrome is and which behavior webkit is
<jarhar> emilio: basically ampersand inside scope rules means :scope and right now chrome seems to ignore the specificity of scope ? you have explicitly written the ampersand
<jarhar> miriam: ampsersand isnt :scope exactly, it refers to the scope root element, and the ampersand refers to the seelector in the scope start. consistent with how those two selectors have worked in other places. ampersand refers to a selector and ? refers to an element?
<jarhar> TabAtkins: the ampersand the referring selector is empty
<jarhar> emilio: ampersand and :scope are effectively the same right?
<jarhar> TabAtkins: behavior is the same except for this case because ones a pseudo class and one is a parent selector. how academic this is is a good question
<jarhar> kizu: you could use it not to just target the scope itself ?
<jarhar> kizu: i managed to do something with it but i dont remember exactly
<jarhar> miriam: what does ampersand do on its own?
<jarhar> TabAtkins: its supposed to match the same element in the parent rule?
<jarhar> miriam: what about root level of a stylesheet
<jarhar> matthieud: the root level - it should be :root and it gets one specificity of the pseudo class
<jarhar> TabAtkins: that is not specified in the spec. the specificity line explicitly refers to the line ? selector list, doesnt say what specificity is
<jarhar> TabAtkins: thats just what browsers are doing on their own right now
<jarhar> miriam: im getting zero specificity in chromium
<jarhar> emilio: another case where gecko and chromium do zero specificity but webkit does use specificity
<emilio> data:text/html,<style>& p { color: green } p { color: blue }</style><p>ABC
<emilio> green in webkit, blue in gecko / blink
<jarhar> astearns: so we need to fix both things
<jarhar> TabAtkins: we can decide on which behavior we like better, but ...
<jarhar> TabAtkins: zero specificity in chrome
<jarhar> miriam: that feels a little strange to me but i dont have an argument why the other would feel less strange. i dont think of it as a zero specificity selector
<jarhar> TabAtkins: doesnt have a specificity to refer to, it just matches the same thing as scope
<jarhar> TabAtkins: not defined in terms of that selector existing having specificity
<jarhar> miriam: if we have it at zero specificity, then we specify everything on ampersand instead of root
<jarhar> miriam: people will say where root to get zero specificity
<jarhar> TabAtkins: the only difference is if ? target root to ? some of the properties with html as a selector. that would lose if it had a pseuco class specificity. in all cases it wouldnt matter
<jarhar> TabAtkins: they can already do it with where
<jarhar> miriam: maybe the scope case is a better one to look at. is it surprising you write styles scoped styles in an embedded stylesheet you use the ampersand, you get zero specificity
<jarhar> miriam: thats where it would become an issue more often because someone woudl be targeting that div
<jarhar> miriam: if you use solely an ampersand it becomes zero outside stylesheets have ? specificitity, scope comes into play, that becomes riskky
<jarhar> TabAtkins: giving it a specificyt of zero pseudo class is easy to override, zero is easier but not by a huge amount
<jarhar> astearns: im hearing lots of strong opinions, how are we going to decide?
<jarhar> TabAtkins: technically chrome and firefox agree and spec. as 2/3 aint bad
<jarhar> emilio: yeah not strong opinion. just some of it - having a ? context selector doesn't increase specificity
<jarhar> emilio: yeah i guess its ok
<emilio> s/? context selector/more complex selector which/
<jarhar> matthieud: i dont have a strtong opionin either, but when you ? a selector more when you add some part to a selector :root i guess itm kaes sense when you are more explicit that you get something from it
<jarhar> matthieud: all this is really edge case i dont know if anyone will check this
<jarhar> miriam: so thats an argument for zero?
<jarhar> astearns: and i believe that the issue about wpt, do they test for zero or something else?
<jarhar> emilio: i think that they test for zero specificity but thats for visit scope not ampersand
<jarhar> astearns: so we could resolve on zeroing both a bare ampersand and scope
<jarhar> astearns: since nobody has a strong opinion about it and they should probably match and we have tests that browsers pass, but with the option of reopening it if we find a use case for doing non-zero at some point in the future
<jarhar> TabAtkins: ultimately authors can do that - they can just put :scope in the @scope selector and that will do it
<jarhar> astearns: and if we find people doing that maybe we can say hey maybe we should be doing hta tautomatically
<jarhar> fantasai: summary of what we're talking about?
<jarhar> TabAtkins: do you want to the proposed resolution?
<jarhar> fantasai: we talked about :scope and ampersand, are we talking about both fo tth eresoultion?
<emilio> explicit :scope is fine, things to discuss are "implicit" scope and bare ampersand
<jarhar> TabAtkins: no, ampersand doesn't have a selector for its parent, when it implicitly selects nothing at all its specificity is zero
<jarhar> astearns: which is tested but not specified
<jarhar> TabAtkins: it is technically specified, not explicitly. it should be more explicit
<jarhar> fantasai: and whats an example of a case where ? selector
<jarhar> TabAtkins: its at the root level or inside a scope which doesnt have a scope start
<jarhar> fantasai: for example inside a style element
<jarhar> TabAtkins:
<jarhar> TabAtkins: right
<jarhar> astearns: there would also be a resolution that the - that scope would match this as well
<jarhar> fantasai: :scope would be zero always or only when its implicit
<jarhar> fantasai: if i have an at scope and a id selector, then when i write foo inside there, im implicitly adding :scope and that adds a pseudo class to it
<jarhar> TabAtkins: no, that was a decision we made on purpose. you just get the selector that you wrote plus the magical scoping behavior
<jarhar> fantasai: so it doesn't inherit the scope start specificity unless you write :scope explicitly
<jarhar> fantasai: so why dont we do that in the case ??
<jarhar> matthieud: its not true for the ? selector
<jarhar> TabAtkins: for general nesting, its expected - if nesting didnt ? the specificity then everything would break. scoping gives us some ? for nesting you assume your rules are more specific
<jarhar> TabAtkins: otherwise &hover would almost never match because the parent rule would override it
<jarhar> fantasai: i was trying to figure out why it would suddenly gain some specificity
<jarhar> miriam: nobody is proposing that
<jarhar> miriam: its not clearly defined in either place, lets make sure its the same
<emilio> s/&hover/&:hover/
<jarhar> TabAtkins: explicitly state in specs that when an ampersand doesn't actually have a parent rule to draw from then its specificity is zero
<jarhar> TabAtkins: explicitly defined in the spec
<jarhar> astearns: any objections?
<jarhar> astearns: do we also have to make a scope resolution?
<jarhar> TabAtkins: neither ampersand nor explicit scope - thats already in the spec
<jarhar> astearns: i think we are done
<astearns> RESOLVED: explicitly state in specs that when an ampersand doesn't actually have a parent rule to draw from then its specificity is zero

@mdubet
Copy link

mdubet commented Jun 11, 2024

So as @emilio said before, we might need to revert "always serialize implicit :scope"
#9621 then ?

@mirisuzanne
Copy link
Contributor

@mdubet I added agenda+ to #9621

@emilio
Copy link
Collaborator

emilio commented Jun 11, 2024

Ah, yes of course.

andruud pushed a commit to andruud/csswg-drafts that referenced this issue Nov 1, 2024
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 1, 2024
The '&' selector, when it has no parent selector to draw from,
has a specificity of zero [1].

Blink already does the right thing. This CL just adds a new WPT
to enforce that behavior, and adjusts an existing tests which
expected specificity to be added.

[1] w3c/csswg-drafts#10196

Bug: 343794754
Change-Id: I3bfdba8a5db66be2d2cdd6f1b2f41b86aadbab9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5982912
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1376861}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 1, 2024
The '&' selector, when it has no parent selector to draw from,
has a specificity of zero [1].

Blink already does the right thing. This CL just adds a new WPT
to enforce that behavior, and adjusts an existing tests which
expected specificity to be added.

[1] w3c/csswg-drafts#10196

Bug: 343794754
Change-Id: I3bfdba8a5db66be2d2cdd6f1b2f41b86aadbab9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5982912
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1376861}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Nov 5, 2024
…evel '&' specificity, a=testonly

Automatic update from web-platform-tests
[nesting] Improve WPT coverage for top-level '&' specificity

The '&' selector, when it has no parent selector to draw from,
has a specificity of zero [1].

Blink already does the right thing. This CL just adds a new WPT
to enforce that behavior, and adjusts an existing tests which
expected specificity to be added.

[1] w3c/csswg-drafts#10196

Bug: 343794754
Change-Id: I3bfdba8a5db66be2d2cdd6f1b2f41b86aadbab9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5982912
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1376861}

--

wpt-commits: 664de243ddb3429237c7d5740b18d5495207c843
wpt-pr: 48929
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Nov 6, 2024
…evel '&' specificity, a=testonly

Automatic update from web-platform-tests
[nesting] Improve WPT coverage for top-level '&' specificity

The '&' selector, when it has no parent selector to draw from,
has a specificity of zero [1].

Blink already does the right thing. This CL just adds a new WPT
to enforce that behavior, and adjusts an existing tests which
expected specificity to be added.

[1] w3c/csswg-drafts#10196

Bug: 343794754
Change-Id: I3bfdba8a5db66be2d2cdd6f1b2f41b86aadbab9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5982912
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1376861}

--

wpt-commits: 664de243ddb3429237c7d5740b18d5495207c843
wpt-pr: 48929
i3roly pushed a commit to i3roly/firefox-dynasty that referenced this issue Nov 6, 2024
…evel '&' specificity, a=testonly

Automatic update from web-platform-tests
[nesting] Improve WPT coverage for top-level '&' specificity

The '&' selector, when it has no parent selector to draw from,
has a specificity of zero [1].

Blink already does the right thing. This CL just adds a new WPT
to enforce that behavior, and adjusts an existing tests which
expected specificity to be added.

[1] w3c/csswg-drafts#10196

Bug: 343794754
Change-Id: I3bfdba8a5db66be2d2cdd6f1b2f41b86aadbab9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5982912
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1376861}

--

wpt-commits: 664de243ddb3429237c7d5740b18d5495207c843
wpt-pr: 48929
mdubet added a commit to mdubet/WebKit that referenced this issue Jan 28, 2025
https://bugs.webkit.org/show_bug.cgi?id=286600

Reviewed by NOBODY (OOPS!).

By CSSWG resolution, top-level nesting parent selector has zero specificity.

w3c/csswg-drafts#10196 (comment)

* LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-specificity-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/top-level-parent-pseudo-specificity-expected.txt:
* Source/WebCore/css/CSSSelector.cpp:
(WebCore::CSSSelector::replaceNestingParentByPseudoClassScope):
webkit-commit-queue pushed a commit to mdubet/WebKit that referenced this issue Jan 28, 2025
https://bugs.webkit.org/show_bug.cgi?id=286600

Reviewed by Ryan Reno.

By CSSWG resolution, top-level nesting parent selector has zero specificity.

w3c/csswg-drafts#10196 (comment)

* LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-specificity-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/top-level-parent-pseudo-specificity-expected.txt:
* Source/WebCore/css/CSSSelector.cpp:
(WebCore::CSSSelector::replaceNestingParentByPseudoClassScope):

Canonical link: https://commits.webkit.org/289455@main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Tuesday afternoon
Development

Successfully merging a pull request may close this issue.

8 participants