You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[css-cascade-6] Should the scope proximity calculation be impacted by nesting scopes? #10795
If two declarations both have elements selected by scoped descendant relationships applying weak scoping proximity, then the declaration with the fewest generational hops between the ancestor/descendant element pair wins.
If multiple such pairs are represented, their weak scoping proximity weights are compared from innermost scoping relationship to outermost scoping relationship (with any missing pairs weighted as infinity).
In order to find a 'proximity', we need both a 'subject' element and a ':scope' element. Then we count the 'steps' between one and the other.
Nested @scope rules are allowed. Each scope rule's <scope-start> selector is 'scoped' by the parent scope rule. If we want scopes to accumulate with nesting, we have to determine which subjects we are comparing to which roots. Given this example:
@scope (a) {
@scope (b) {
c { /* … */ }
}
}
I see two options (though I believe they might be functionally the same??). The scope proximity weight for c is one of:
[c-to-b-distance, c-to-a-distance]
[c-to-b-distance, b-to-a-distance]
In either case, I believe the proposal is to compare proximities from inner-most to outer-most.
But why?
I think this would be a reasonable approach. At least, it makes some sense to me that things might work this way. But I can't think of an actual use-case where I would rely on this behavior. I'm not opposed, but I'm also not sure how useful or complex it is.
Thinking through it a bit more, and discussing with @argyleink, I don't really have any reason not to do this. The alternative is falling back on source order, which isn't better than multi-step proximity.
And I believe there's no difference between the two approaches above. The distance between two roots will always be equivalent to the additional distance between a subject and ancestor root. So the approach seems straight-forward.
So unless there's push back from other implementors (@andruud made the initial change here?), I'm going to propose we resolve on @mdubet's proposal here. Marking this as agenda+ to try and get that resolution.
@mirisuzanne In other words, this would introduce a dynamic number of cascade criteria (for the first time)? A bit like specificity, but instead of (A,B,C), it's a variable number of components.
But why? [...]
Last time this came up, we concluded that: 1) it adds complexity (both for impl and authors' mental model), and 2) it's not useful. Your answer to this question suggests that nothing has changed. Therefore, I do oppose this change, as it seems to (at best) only be about theoretical purity at the expense of other things.
I'm also not sure how [...] complex it is
We'd ideally investigate that a little bit before making any moves spec wise. @scope also shipped a long time ago in Blink. I would need to be able to prove that we even can ship such a change without breakage. Otherwise, we might end up with subtly different cascade behaviors forever, which is worse than just aligning on the current spec.
I'm going to propose we resolve on @mdubet's proposal here
We should minimally first answer the "But why?" with an actual answer, and explain why the more complex behavior is useful after all.
As for adding a dynamically-sized cascade criteria... I generally agree with @andruud - Concerned about complexity on implementations/authors. Could adding a count of nested @scope work as an approximation that does not require dynamic sizing?
To flesh that proposal out a bit, we'd have a (consistently) two-part value, including:
proximity steps to the nearest root
count of active scopes
In your example, the first rule has a scope of [1,2] and the second has a scope of [1,1]. As with specificity, we would compare those one at a time - moving to the scope-count as a tie-breaker only when the proximity is equal.
For the use cases where this applies, can't author use layers? e.g.
Could adding a count of nested @scope work as an approximation that does not require dynamic sizing?
That also seems good to me.
I actually wonder if scope proximity is all that useful to begin with..
Otherwise, we could actually consider removing proximity entirely (as @emilio is hinting at). I kind of regret not making @scope just about scoping.
Proximity is actually the CSS feature we are most excited about.
It is very common for us to have components and layouts with areas that can contain other components. With both potentially having content from a wysiwyg editor. Many CMS's are moving towards very flexible page builders where content editors can nest components in various ways.
The only way to style these as designers would expect them to appear is by using @scope and if @scope has proximity.
Currently all three proposals above would give the same result, since the proximity of the first step provides a clear winner (a->c is fewer steps than b->c). If we prioritize number of scopes above proximity, we would reverse the result. But at that point we're just re-creating a more blunt form of specificity, right? We're saying that the number or selectors involved should matter more than their proximity in the dom. Which is why we opted for proximity-after-specificity in the first place. And it seems to me (maybe this is what @5t3ph was getting at) that the real concern here is with scopes not adding specificity.
The reason we would expect blue to win, is because we expect specificity.
So @noamr I don't know if you were confused, but I agree with the statement. I don't know that the problem here is how we've defined proximity - but the fact some people might want scopes to increase specificity along the way (which they can do by using &). Or, as you suggest, could use layers. Because the confusion isn't about proximity at all, but wanting to override it sometimes.
So @noamr I don't know if you were confused, but I agree with the statement. I don't know that the problem here is how we've defined proximity - but the fact some people might want scopes to increase specificity along the way (which they can do by using &). Or, as you suggest, could use layers. Because the confusion isn't about proximity at all, but wanting to override it sometimes.
I meant that layers don't solve the use case presented in the codepen. But I'm not sure what does exactly and what's the right way to approach it; I would suggest to try to look at the use case of compoents-in-components more holistically rather than jump to a solution that overloads specificity with yet another implicit rule.
But at that point we're just re-creating a more blunt form of specificity, right?
Hm, there is a divergence, though? As in, more specific scope selectors don't necessarily equate to "more deeply nested?" e.g. @scope (.foo.bar.baz) { .target { /*...*/ } } versus @scope(.a) { @scope (.b) { .target { /*...*/ } } }
Also, we're firmly in the realm of edge cases, but if there's an implicit selector in the nesting, what's the proposed specificity? e.g. @scope (.a) { @scope (.b) { @scope { .target { /*...*/ } } } }
Hm, there is a divergence, though? As in, more specific scope selectors don't necessarily equate to "more deeply nested?" e.g. @scope (.foo.bar.baz) { .target { /*...*/ } } versus @scope(.a) { @scope (.b) { .target { /*...*/ } } }
They're not identical. But it's a similar heuristic to approximate the narrowness of the selector targeting, rather than anything more nuanced about the DOM structure (which we can't currently do).
Also, we're firmly in the realm of edge cases, but if there's an implicit selector in the nesting, what's the proposed specificity? e.g. @scope (.a) { @scope (.b) { @scope { .target { /*...*/ } } } }
Currently scope roots have no impact on specificity, so .target gives us a specificity of [0,1,0]. The implicit selector there only works in a DOM-nested context, and doesn't impact the specificity calculation.
The CSS Working Group just discussed [css-cascade-6] Should the scope proximity calculation be impacted by nesting scopes?, and agreed to the following:
RESOLVED: Close no change for now, unless someone comes back with clear use cases + proposal
RESOLVED: Republished a new WD of css-cascade-6
The full IRC log of that discussion
<emilio> miriam: we've discussed a few times, and there's a request for more research but unsure what the research should be
<emilio> ... right now we flatten scope proximity to a single value based on nearest scope
<emilio> ... it's probably a decent proxy for that
<emilio> ... nobody knows what the use case for nested scopes is, but you can do it
<emilio> ... not clear that is different from narrowing the scope
<emilio> ... I think the flat version works ok
<emilio> ... it'd be reasonable to lookup nested scope, but would be difficult to implement, and probably not necessary for authors
<emilio> ... other proposals in the thread I didn't quite like
<matthieud> q+
<emilio> emilio: so proposal is no change?
<emilio> miriam: I'd go for that but open to other options
<astearns> ack fantasai
<TabAtkins> +1 to no change
<emilio> +1
<TabAtkins> q+
<emilio> fantasai: I think what would happen most likely is for nesting scopes like different color schemes
<emilio> ... and I have wide / narrow layout, in a way they nest
<emilio> ... what's likely to happen is that the author was thinking about narrowing
<matthieud> @scope my-color-theme-purple { @ scope dark { ... color: something-dark-and-purple }}
<emilio> ... and so for some rules you care about the outer scope and for others the inner
<emilio> ... and we don't know which one is important
<emilio> ... I think we don't know the answer about which one is important
<emilio> ... in that thing they're likely to be different thing
<emilio> ... that's why we have both specificity and layering as escape hatches
<emilio> fantasai: neither layering or specificity control it because the question is which type of container I'm closest to (dark or light for example)
<emilio> ... if you have nested scopes you might have dark -> disabled or whatever
<emilio> ... but you might not know which proximity is more important
<emilio> ... I remember looking at it and thinking it can't be obvious which one to pick
<emilio> ... it's clear that if you're scoped double into something you want it more heavily weighted that only one level
<emilio> ... but in which way it's not clear
<emilio> miriam: hard to respond without an example but remember that scope is per-selector
<emilio> ... so I can ?? and then they'd look at the right scope
<emilio> ... and I can define a scope for my color and for my components and so on
<emilio> ... and they'd respond to the proximity of their scope independently
<astearns> ack matthieud
<emilio> matthieud: if you have a scope for a purple theme and then one for the dark, you are interested in both
<emilio> ... and at the beginning the proximity was ignored for the second one
<emilio> ... [example similar to fantasai]
<emilio> miriam: you need to care about both for it to become an issue
<emilio> ... for that example to become an issue you'd need dark -> light and purple -> green
<emilio> matthieud: which I guess can happen?
<emilio> ... but again these are only test-cases I write
<emilio> miriam: I think it's a bit of a theoretical example
<emilio> ... question would be would any of the other proposals solve it?
<emilio> ack TabAtkins
<astearns> ack TabAtkins
<emilio> TabAtkins: from what I can tell, that afaict is solved equally well by just not nesting scopes
<emilio> ... just single scopes with .purple.dark, .green.light, .green.dark, .green.dark
<emilio> matthieud: they might not be in the same element
<emilio> TabAtkins: ah, was wrong
<emilio> ... but larger concern is that people find specificity complicated
<emilio> ... but the more we try to add specificity-ish things...
<emilio> ... adding complexity here is going to give unexpected answers
<andruud> +1
<emilio> ... eg when they're interleaved
<emilio> ... so I'm concerned with adding more complexity
<emilio> ... given not every relationship can be represented here
<emilio> ... my opinion is that it's not worth the complexity + performance cost
<emilio> ... suggested middle-of-the-road compromise where number-of-scopes was also part of the calculation
<kizu> https://codepen.io/kizu/pen/gbaeprG — example
<emilio> ... but that also doesn't fix any of the issues
<emilio> lea: so what are you proposing
<emilio> TabAtkins: no change to the spec
<emilio> matthieud: we filter by both but proximity of the inner only
<emilio> fantasai: I think something's off but don't know how to fix it
<emilio> astearns: never has been clear who's going to do the research and dig into other solutions
<emilio> ... so I think we should close no change for now
<emilio> PROPOSED: Close no change for now
<emilio> fantasai: as long as it's clear that can be reopened if a new proposal comes up
<fantasai> PROPOSED: Close no change for now, unless someone comes back with clear use cases + proposal
<emilio> RESOLVED: Close no change for now, unless someone comes back with clear use cases + proposal
<emilio> miriam: this was the last issue of scope
<emilio> astearns: should we republish?
<emilio> miriam: it's already a WD
<emilio> astearns: should we resolve publishing a new WD with the intent that it becomes CR soonish?
<miriam> (last issue besides proposals for level 2 features)
<emilio> matthieud: yeah if we change proximity it's going to be a webcompat issue
<fantasai> level 7? :)
<emilio> RESOLVED: Republished a new WD of css-cascade-6
<emilio> s/Republished/Republish
<miriam> yes, level 7
Activity
mirisuzanne commentedon Aug 29, 2024
Thinking through it a bit more, and discussing with @argyleink, I don't really have any reason not to do this. The alternative is falling back on source order, which isn't better than multi-step proximity.
And I believe there's no difference between the two approaches above. The distance between two roots will always be equivalent to the additional distance between a subject and ancestor root. So the approach seems straight-forward.
So unless there's push back from other implementors (@andruud made the initial change here?), I'm going to propose we resolve on @mdubet's proposal here. Marking this as agenda+ to try and get that resolution.
andruud commentedon Aug 30, 2024
@mirisuzanne In other words, this would introduce a dynamic number of cascade criteria (for the first time)? A bit like specificity, but instead of (A,B,C), it's a variable number of components.
Last time this came up, we concluded that: 1) it adds complexity (both for impl and authors' mental model), and 2) it's not useful. Your answer to this question suggests that nothing has changed. Therefore, I do oppose this change, as it seems to (at best) only be about theoretical purity at the expense of other things.
We'd ideally investigate that a little bit before making any moves spec wise. @scope also shipped a long time ago in Blink. I would need to be able to prove that we even can ship such a change without breakage. Otherwise, we might end up with subtly different cascade behaviors forever, which is worse than just aligning on the current spec.
We should minimally first answer the "But why?" with an actual answer, and explain why the more complex behavior is useful after all.
emilio commentedon Oct 8, 2024
cc @dshin-moz
dshin-moz commentedon Oct 9, 2024
So given something like
and given below rules:
The concern is that the applied
.styledwould depend purely on the order of declaration, right?FWIW, authors that want this could coax this out by using
&and relying on specificity, becoming very CSS Nesting-like:dshin-moz commentedon Oct 9, 2024
As for adding a dynamically-sized cascade criteria... I generally agree with @andruud - Concerned about complexity on implementations/authors. Could adding a count of nested
@scopework as an approximation that does not require dynamic sizing?mirisuzanne commentedon Oct 9, 2024
To flesh that proposal out a bit, we'd have a (consistently) two-part value, including:
In your example, the first rule has a scope of
[1,2]and the second has a scope of[1,1]. As with specificity, we would compare those one at a time - moving to the scope-count as a tie-breaker only when the proximity is equal.I would be happy with that as an approximation.
7 remaining items
noamr commentedon Oct 23, 2024
For the use cases where this applies, can't author use layers? e.g.
Can't this be made more explicit with layers representing where the style comes from, rather than with something implicit like proximity?
romainmenke commentedon Oct 23, 2024
How do you mean?
Would this take DOM structure into account?
noamr commentedon Oct 23, 2024
Sorry, I misunderstood the use case at first, please disregard.
mirisuzanne commentedon Oct 23, 2024
The use-case mentioned on the call is:
With the DOM:
Currently all three proposals above would give the same result, since the proximity of the first step provides a clear winner (
a->cis fewer steps thanb->c). If we prioritize number of scopes above proximity, we would reverse the result. But at that point we're just re-creating a more blunt form of specificity, right? We're saying that the number or selectors involved should matter more than their proximity in the dom. Which is why we opted for proximity-after-specificity in the first place. And it seems to me (maybe this is what @5t3ph was getting at) that the real concern here is with scopes not adding specificity.The reason we would expect
blueto win, is because we expect specificity.So @noamr I don't know if you were confused, but I agree with the statement. I don't know that the problem here is how we've defined proximity - but the fact some people might want scopes to increase specificity along the way (which they can do by using
&). Or, as you suggest, could use layers. Because the confusion isn't about proximity at all, but wanting to override it sometimes.noamr commentedon Oct 23, 2024
I meant that layers don't solve the use case presented in the codepen. But I'm not sure what does exactly and what's the right way to approach it; I would suggest to try to look at the use case of compoents-in-components more holistically rather than jump to a solution that overloads specificity with yet another implicit rule.
dshin-moz commentedon Oct 24, 2024
Hm, there is a divergence, though? As in, more specific scope selectors don't necessarily equate to "more deeply nested?" e.g.
@scope (.foo.bar.baz) { .target { /*...*/ } }versus@scope(.a) { @scope (.b) { .target { /*...*/ } } }Also, we're firmly in the realm of edge cases, but if there's an implicit selector in the nesting, what's the proposed specificity? e.g.
@scope (.a) { @scope (.b) { @scope { .target { /*...*/ } } } }mirisuzanne commentedon Oct 24, 2024
They're not identical. But it's a similar heuristic to approximate the narrowness of the selector targeting, rather than anything more nuanced about the DOM structure (which we can't currently do).
Currently scope roots have no impact on specificity, so
.targetgives us a specificity of[0,1,0]. The implicit selector there only works in a DOM-nested context, and doesn't impact the specificity calculation.css-meeting-bot commentedon Aug 19, 2025
The CSS Working Group just discussed
[css-cascade-6] Should the scope proximity calculation be impacted by nesting scopes?, and agreed to the following:RESOLVED: Close no change for now, unless someone comes back with clear use cases + proposalRESOLVED: Republished a new WD of css-cascade-6The full IRC log of that discussion
<emilio> miriam: we've discussed a few times, and there's a request for more research but unsure what the research should be<emilio> ... right now we flatten scope proximity to a single value based on nearest scope
<emilio> ... it's probably a decent proxy for that
<emilio> ... nobody knows what the use case for nested scopes is, but you can do it
<emilio> ... not clear that is different from narrowing the scope
<emilio> ... I think the flat version works ok
<emilio> ... it'd be reasonable to lookup nested scope, but would be difficult to implement, and probably not necessary for authors
<emilio> ... other proposals in the thread I didn't quite like
<matthieud> q+
<emilio> emilio: so proposal is no change?
<emilio> miriam: I'd go for that but open to other options
<astearns> ack fantasai
<TabAtkins> +1 to no change
<emilio> +1
<TabAtkins> q+
<emilio> fantasai: I think what would happen most likely is for nesting scopes like different color schemes
<emilio> ... and I have wide / narrow layout, in a way they nest
<emilio> ... what's likely to happen is that the author was thinking about narrowing
<matthieud> @scope my-color-theme-purple { @ scope dark { ... color: something-dark-and-purple }}
<emilio> ... and so for some rules you care about the outer scope and for others the inner
<emilio> ... and we don't know which one is important
<emilio> ... I think we don't know the answer about which one is important
<emilio> ... in that thing they're likely to be different thing
<emilio> ... that's why we have both specificity and layering as escape hatches
<emilio> fantasai: neither layering or specificity control it because the question is which type of container I'm closest to (dark or light for example)
<emilio> ... if you have nested scopes you might have dark -> disabled or whatever
<emilio> ... but you might not know which proximity is more important
<emilio> ... I remember looking at it and thinking it can't be obvious which one to pick
<emilio> ... it's clear that if you're scoped double into something you want it more heavily weighted that only one level
<emilio> ... but in which way it's not clear
<emilio> miriam: hard to respond without an example but remember that scope is per-selector
<emilio> ... so I can ?? and then they'd look at the right scope
<emilio> ... and I can define a scope for my color and for my components and so on
<emilio> ... and they'd respond to the proximity of their scope independently
<astearns> ack matthieud
<emilio> matthieud: if you have a scope for a purple theme and then one for the dark, you are interested in both
<emilio> ... and at the beginning the proximity was ignored for the second one
<emilio> ... [example similar to fantasai]
<emilio> miriam: you need to care about both for it to become an issue
<emilio> ... for that example to become an issue you'd need dark -> light and purple -> green
<emilio> matthieud: which I guess can happen?
<emilio> ... but again these are only test-cases I write
<emilio> miriam: I think it's a bit of a theoretical example
<emilio> ... question would be would any of the other proposals solve it?
<emilio> ack TabAtkins
<astearns> ack TabAtkins
<emilio> TabAtkins: from what I can tell, that afaict is solved equally well by just not nesting scopes
<emilio> ... just single scopes with .purple.dark, .green.light, .green.dark, .green.dark
<emilio> matthieud: they might not be in the same element
<emilio> TabAtkins: ah, was wrong
<emilio> ... but larger concern is that people find specificity complicated
<emilio> ... but the more we try to add specificity-ish things...
<emilio> ... adding complexity here is going to give unexpected answers
<andruud> +1
<emilio> ... eg when they're interleaved
<emilio> ... so I'm concerned with adding more complexity
<emilio> ... given not every relationship can be represented here
<emilio> ... my opinion is that it's not worth the complexity + performance cost
<emilio> ... suggested middle-of-the-road compromise where number-of-scopes was also part of the calculation
<kizu> https://codepen.io/kizu/pen/gbaeprG — example
<emilio> ... but that also doesn't fix any of the issues
<emilio> lea: so what are you proposing
<emilio> TabAtkins: no change to the spec
<emilio> matthieud: we filter by both but proximity of the inner only
<emilio> fantasai: I think something's off but don't know how to fix it
<emilio> astearns: never has been clear who's going to do the research and dig into other solutions
<emilio> ... so I think we should close no change for now
<emilio> PROPOSED: Close no change for now
<emilio> fantasai: as long as it's clear that can be reopened if a new proposal comes up
<fantasai> PROPOSED: Close no change for now, unless someone comes back with clear use cases + proposal
<emilio> RESOLVED: Close no change for now, unless someone comes back with clear use cases + proposal
<emilio> miriam: this was the last issue of scope
<emilio> astearns: should we republish?
<emilio> miriam: it's already a WD
<emilio> astearns: should we resolve publishing a new WD with the intent that it becomes CR soonish?
<miriam> (last issue besides proposals for level 2 features)
<emilio> matthieud: yeah if we change proximity it's going to be a webcompat issue
<fantasai> level 7? :)
<emilio> RESOLVED: Republished a new WD of css-cascade-6
<emilio> s/Republished/Republish
<miriam> yes, level 7