When you first hear about css @scope, you think: so what does it actually scope? And the answer is that it scopes where the css applies.
You also ask yourself, why do we need that? What is the difference between @scope (.parent) { .child { … } } and .parent .child { … } or .parent { & .child { … } }? And the answer is: not much difference there, except a few nuances:
- you can add a lower boundary, like so:
@scope (<scope start selector>) to (<scope end selector>) { … }(the end element and its subtree are excluded) - specificity of scoped selectors ends up to be as writen
What @scope doesn’t do:
- it does affect cascade due to proximity based conflict resolution, but it doesn’t encapsulate styles the way Shadow DOM does
- it doesn’t affect property inheritance (inherited values can pass into and out of the scoped subtree)
@scope adds a very useful element to the cascade, which is proximity based conflict resolution—another layer of cascade resolution.
Current cascade order: cascade layer → specificity → scoping proximity → order of appearance (source order).
Initially we had only specificity → source order, then cascade layers were introduced as a way to define cascade at higher level of abstraction.
Scoping proximity in the cascade: when competing declarations come from different scopes, the one whose scope root is closer to the element wins, even if source order would otherwise decide.
There is also “inline” @scope, which I find really cool—until now there was a little reason to enclose <style> into a particular element; now you can do this:
<parent>
<style>
@scope { /* root is the <style>'s enclosing parent */ }
</style>
</parent>