[CSSWG] Minutes Scope Breakout 2025-07-02 [css-cascade-6]

=========================================
  These are the official CSSWG minutes.
  Unless you're correcting the minutes,
 please respond by starting a new thread
   with an appropriate subject line.
=========================================


Scope Breakout
--------------

  - RESOLVED: Close no change (Issue #12418: Should/can relative
              selectors be allowed un-nested)
  - The proposed resolution from the call for issue #9740 (`&` matching
      inside the `@scope`, and its interaction with `:scope`) was `& in
      @scope behaves as :where(:scope)`. However, there were a lot of
      options and a lot of opinions, including some not represented on
      the call. Therefore, the team will hold on resolving for one week
      to give folks a chance to review and provide input.

===== FULL MEETING MINUTES ======

Agenda: https://github.com/orgs/w3c/projects/113/views/1

Present:
  Kevin Babbitt
  David Baron
  Justin Breiland
  Oriol Brufau
  Emilio Cobos Álvarez
  Yehonatan Daniv
  Matthieu Dubet
  Elika Etemad
  Anders Hartvoll Ruud
  Roman Komarov
  Romain Menke
  Alan Stearns
  Miriam Suzanne

Scribe: fantasai
Scribe's scribe: emilio

Scope Breakout
==============

Should/can relative selectors be allowed un-nested
--------------------------------------------------
  github: https://github.com/w3c/csswg-drafts/issues/12418

  fantasai: Might've been useful to have Tab at this breakout

  miriam: This was raised by Lea wrt if a whole file is scoped on import
  miriam: If those items are relative selectors ...
  miriam: Should we allow relative selectors at every level of CSS
          including the root?
  miriam: If so, what would it mean?
  miriam: My leaning is that we don't need to do this
  miriam: Tab had some concerns with it
  miriam: For example, a relative selector with a child combinator is
          meaningless in relation to the root
  miriam: similarly ??
  miriam: And normal descendant selector doesn't need to be relative
          until imported into a scope
  miriam: So not really combinators that are meaningful to do at the
          root level

  matthieud: what's the point of having relative top level selectors?
  matthieud: if we allow these is it only working when we `@import`
             scope() something?
  miriam: leaverou was expecting it to be relative to `:root` by
          default, but as tab pointed out that's not particularly useful
  miriam: so that's why I suggest no-change
  miriam: once we import in scopes I think people may want to do it but
          I don't think it makes sense to make it valid or not
          depending on the way it's important
  <kizu> +1 to no change
  matthieud: agree that they can just wrap in `:scope`
  miriam: or prefix each selector yeah
  fantasai: I think it's fine
  emilio: +1 fwiw

  astearns: if we're wrong and this is something people are going to
            use often
  astearns: and litter their sheets with `:scope`, how frustrating is
            that going to be?
  miriam: not sure who's going to complain about it
  miriam: but I think it's more explicit when you litter it with
          `:scope`
  miriam: so I think it's still the right choice
  miriam: it also raises the question of also allowing bare
          declarations in `@scope`, do we want to allow bare
          declarations in a css file?
  <kizu> +1, explicit > implicit
  emilio: You don't have to use :scope everywhere, right, can just wrap
          whole style sheet in :scope {
  emilio: so even if you wanted to do this, only need to add 2 lines of
          CSS ... and technically don't even need the closing bracket
  miriam: only needed for sibling or child combinator. Most styles are
          descendant combinators anyway
  miriam: and sibling is out of scope

  andruud: I'm happy with the discussion so far
  <romain> +1 to no change
  <emilio> +1
  astearns: Proposed resolution, closed no change. Objections?

  RESOLVED: Close no change.

`&` matching inside the `@scope`, and its interaction with `:scope`
-------------------------------------------------------------------
  github: https://github.com/w3c/csswg-drafts/issues/9740

  miriam: Inside of @scope, & and :scope do similar things
  miriam: but slightly different
  miriam: partially because of how nesting and scoping were designed
  miriam: wanted & to behave consistently
  miriam: so it refers to the scope root
  miriam: *selector*
  miriam: and takes on the specificity of that selector
  miriam: :scope is a bit different
  miriam: it has specificity of a pseudo-class, and matches exactly one
          element -- the root element of the scope
  miriam: it's not a stand-in for the selector, it targets a specific
          element
  miriam: question was if those should be more the same in some way

  kizu: I proposed two options, I think 2nd is more clear
  kizu: If you're inside a scope, and you only mention &, then you are
        mentioning that specific root node, same as scope
  kizu: but it might be weird if you have two &
  miriam: You wouldn't be able to do & + &
  miriam: My tendency here is to keep it how it is, because I think all
          of the solutions are potentially confusing
  miriam: and at least this one is consistent with how & works in other
          places
  miriam: but open to discussion

  astearns: Roman, your main concern is author mistakes?
  kizu: yeah
  <ydaniv> I think +1 to kizu's choice
  kizu: Also, if it's an &, is it equal to & in scope?
  kizu: otherwise where does the implicit as :scope go?
  kizu: In my tests, this &, matched something outside of the scope
  kizu: so you could target context outside the scope, and it could
        match an element inside the scope
  kizu: it's a weird interaction
  kizu: is it the same as &, where it still matches scope root; or if
        not, should it match anything outside the scope?
  <kizu> `& h1`
  miriam: It's a scoped selector, so matching anything outside would be
          a bug
  miriam: it's not exactly scope ... matching anything that that
          selector matches as long as it is in the scope
  miriam: So scope doesn't necessarily end where it starts
  kizu: You can inside a scope you can make adjustment on element
        outside the scope
  kizu: because you only check whether the subject of the selector is
        contained by the scope, not other elements in the selector
  kizu: If we do inside a scope ...
  kizu: Do we match only on :hover of the :scope, or will it match
        something outside the scope?
  kizu: You could argue both ways, and Safari and chrome differ last
        time I tested
  emilio: :hover case shouldn't match anything outside the scope
  kizu: here's a better example
  <kizu> `@scope … { &:hover { … } }`
  <kizu> `@scope … { &:hover :scope { … } }`
  miriam: is that confusing though? We've said explicitly where the
          scope is
  kizu: There was a difference in implementation, so we should clarify
        either way
  miriam: More confusing if you did &:hover p

  <matthieud> @scope … { &:hover :scope { … } } Can this actually match
              something ?

  emilio: Can you remind me what the implicit selector meant?
  <emilio> `& > foo`, `> foo`?
  emilio: If you don't write an ampersand, it's :scope right?
  miriam: Yes
  emilio: I think that difference is very confusing.
  emilio: To me it would make sense if & was always :scope
  emilio: Not sure how others feel about that
  kizu: Currently everyone is doing the same, so could close it no
        change, because at least it's interoperable right now
  miriam: Emilio, would you expect them to have the same specificity?
          or different specificity but matching the same element
  emilio: What do we do for nesting, we add the specificity, right?
  emilio: So I would expect specificity here to work the same
  emilio: ... it's not great

  ydaniv: In terms of specificity, I think the fact that they do match,
          to me it's intuitive
  <fantasai> what matches what?
  ydaniv: Authors still see it as appending stuff
  ydaniv: Emilio's example, that the have the same specificity
  astearns: You don't see an issue in changing the & specificity based
            on location?
  ydaniv: If it's the root of the scope, and & matches an implicit
          :scope selector ...
  ydaniv: so question is whether the scope-start selector or implicitly
          just :scope?
  emilio: I think there are 3 possible answers. We have 3 related
          selectors
  emilio: There's <scope-start>, i.e. the selector in the @scope ()
          prelude
  emilio: The there's :scope
  emilio: And then there's :where(:scope)
  emilio: Right now we're using all three of them
  emilio: & means the first
  emilio: implicit selector means :where(:scope), right?
  emilio: and then you can type :scope
  emilio: It's all fairly confusing to me, that they do different things
  emilio: than a nested selector
  emilio: That implicit nested selectors behave differently than
          explicit nested selectors
  <fantasai> +1

  miriam: Part of what creates that is that :scope has specificity and
          & has specificity and they're different
  miriam: so what should implicit do?
  emilio: Couldn't we define & to be :where(:scope), so at least it's
          consistent with implicit just as in nested?
  miriam: The @scope rule on its own doesn't add specificity, so we
          could say reasonably, the & representing the parent is zero
          since scope rule has zero specificity
  emilio: That also explains nested selectors within @scope
  emilio: @scope { foo ... } that's same as @scope { :where(:scope)
          foo ... } right?
  miriam: That brings us back to Roman's question of whether & matches
          the scope root, or other elements that happen to match the
          same selector
  emilio: Make it match the scope root
  <kizu> > The & selector is defined to represent the selector
         representing the scoping root (the <scope-start> selector), or
         else :scope if no selector was specified.
  miriam: that would be different from other places where we represent
          the enclosing selector
  kizu: & is designed to represent the selector of the scoping root,
        not the scoping root
  kizu: Right now they could consider both the specificity of the
        scoping root *and* ???
  kizu: So maybe we adjust to make it represent the scoping root
  miriam: So proposal is to make & represent :where(:scope)
  kizu: Yes. But we will need to see if it will not break something.
  emilio: It's behavior change either way
  miriam: I'm ok with this change
  kizu: I think it would be better

  romain: I'm unsure if removing specificity here is the right call
  romain: I think authors will expect the specificity, and it will be
          weird to remove it
  romain: Adding selectors makes things more specific. Not adding
          specificity when using & feels weird to me.
  miriam: Even though scope root selector adds no specificity by
          default?
  romain: but :scope has specificity, it's only wrapping in an at-rule
          that doesn't have specificity
  miriam: Are you saying have it be the same as :scope?
  romain: Maybe.
  romain: Writing a selector and having it produce zero specificity
          feels weird to me.
  romain: I don't think there's any other selector that behaves like
          that

  astearns: I'm also a little concerned about difference of & in @scope
            vs nesting
  astearns: but also, is there a way to get the specificity of the
            @scope prelude, if we make this change?
  miriam: No
  matthieud: No, and I think that was the reason for this. It allows
             authors to opt into this specificity
  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.
  astearns: Do we believe authors will no longer need that option?
  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
  fantasai: if we were clearer about what specificity different things
            have it'd be less confusing
  fantasai: I think the problem is that it is so implicit
  miriam: It's implicit, but it's also following existing rules.
  miriam: The implicit one is the only new one
  <kizu> This is what we have right now:
  <kizu> - `@scope (article) { h1 {} }` — 0.0.1
  <kizu> - `@scope (article) { & h1 {} }`— 0.0.2
  <kizu> - `@scope (article) { :scope h1 {} }` — 0.1.1

  <astearns> :scope-selector
  astearns: if we make this change, and then we do find that people
            need access to the prelude selector's specificity, what are
            our options?
  astearns: do we need a new pseudo, that targets the scope selector?
  kizu: Also some proposal to have a scope-end selector, so adding
        scope-start wouldn't be unreasonable

  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 &
          in child
  emilio: it differs from nesting in a really subtle way
  emilio: can do anything, but feels odd to me ... I understand why we
          don't want @scope { } to increase specificity
  emilio: adding specificity of :scope, that would be a problem
  emilio: but it's not great!
  emilio: I'm ok even with no change, but it's very weird behavior
  emilio: explaining it to people is going to be very "fun"
  miriam: Other part
  miriam: Does & match things other than scope root?

  fantasai: I think there's 3 ways that could go
  fantasai: we could say & matches things other than the scope root
            anywhere
  fantasai: that it only matches the scope root
  fantasai: or it matches things that match the scope root but only
            inside the scope
  fantasai: If you do `foo bar` inside `@scope` can foo match inside
            the scope?
  <kizu> Related post: https://blog.kizu.dev/scope-selector-nuance/ :)
  miriam: no because of the implicit scope selector, so only if you add
          `&` or `:scope` somewhere
  emilio: That implicit nesting means that ..
  <emilio> `@scope (..) { foo bar { ... } }` is basically
           `:where(:scope) foo bar { ... }`
  <fantasai> :where(:scope) foo bar, foo:where(:scope) bar, foo
             :where(:scope) bar ?
  miriam: No, it has the implicit `:where(:scope)` at the front
  miriam: unless you put the & somewhere else, so same as scope
  fantasai: So & represents the scope-start selector + :where(:scope) ?
  kizu: that's the question
  emilio: no, only scope-start
  <kizu> `&:hover :scope`
  fantasai: what miriam said is that if it doesn't represent `:scope`
            it can expand the scope
  miriam: it can match outside of the scope
  emilio: because `&` prevents the implicit `:where(:scope)` from
          getting inserted

  astearns: Should we go back to Miriam's idea of choosing what &
            matches, before we decide on specificity?
  astearns: I kinda like the idea that it can only match the scope root.
  astearns: idea of matching other things inside the scope root seems
            weird to me
  emilio: I think it's fine, but pretty tied to the specificity question
  emilio: would be nice not to define it as something magic
  emilio: we've explained it as :is(...)
  emilio: be nice to say it's just :where(:scope) or :scope, or ...
  emilio: Another option would be & doesn't avoid injecting that
          implicit scope selector, that might be weird in other ways
  emilio: and doesn't quite mean that it only matches scope root, just
          means selector can't escape the scope root
  emilio: which btw, is there a way of explicitly escaping the scope
          root?
  emilio: other than :where(:scope, :not(:scope))/ (or so)
  <romain> <div class="row">
  <romain> ...
  <romain> <div class="row">
  <romain> @scope (.row) {
  <romain> & :scope { ... }
  <romain> }
  <romain> Is this the question at hand?
  <romain> That & is ".row" and therefor this selector can match?
  <matthieud> (The subject can never escape the scope though, whatever
              the selector of the scoped rule be)

  astearns: What should we do?
  miriam: maybe write out all the options and take it back to the issue?
  astearns: Questions we have are 1. What exactly does the & represent.
            Seems like we have 4 options.
  astearns: and then 2. What specificity is added when you use an &
  astearns: Do we have just 2 options here?
  miriam: Emilio wanted it to match answer to first question
  astearns: Depending on answer to first question, specificity of &
            inside @scope may or may not match how it works in nesting
  miriam: since defining as specificity of parent selector, there's at
          least a match there
  emilio: Third question, should an & keep preventing the inclusion of
          the implicit :scope at the start of the selector
  astearns: which also may be informed by what we have it represent
  emilio: Right. if we choose that it is :scope or :where(:scope)
  matthieud: Different behavior for nesting and @scope

  emilio: I think my preference would be to define the parent selector
          as :where(scope)
  emilio: i.e. make & as :where(:scope)
  emilio: then it's like nesting, you always unconditionally add it
          unless it's already existing somewhere else
  emilio: It does lose the scope-start specificity, but I think
          that's OK
  emilio: because that's the behavior you get without &
  <kizu> +1 to make it :where(:scope)
  miriam: [missed]
  emilio: I think that would be the more consistent solution
  emilio: to say that & is :where(:scope)
  astearns: My concern with merely doing that is that this issue...
            nothing gets done
  astearns: We could resolve that & matches :where(:scope) and then
            allow the "being wrong on the internet" effect to elicit
            responses?
  miriam: I think it already worked to list out the options, and here
          we are back to take a resolution
  astearns: Anyone with concerns wrt & behaving as :where(:scope)?
  astearns: Like more time to work through examples etc.?

  andruud: It remains to be seen whether we can actually do it, since
           @scope has shipped
  emilio: Not yet shipping in FF
  fantasai: I think my suggestion would be to take a tentative
            resolution to give a chance to people not here
  <ydaniv> sounds good
  fantasai: or on vacation
  fantasai: gives a bit more time to people not here
  <emilio> +1
  <kizu> +1
  astearns: Ok, let's take a resolution and say that we will revisit in
            a week to confirm

  PROPOSED: & in @scope behaves as :where(:scope). We will revisit this
            in 1 week's time.

  fantasai: Can Miriam or someone else post a blog post or something,
            to see if anyone has comments?
  fantasai: can be short, like 3 paragraphs + point to issue
  [miriam and roman are maybe going to try]

Received on Wednesday, 2 July 2025 23:33:47 UTC