The following selectors have the same specificity (1,0,1):
@scope (#hero) {
img { border-radius: 50%; }
}
#hero img { border-radius: 50%; }
But because <{img}> is scoped,
it is weighted more strongly in the cascade.
Many existing tools implement "scoped styles"
by applying a unique class or attribute
to every element in a given scope
or "single file component."
In this example there are two scopes
(
main-component
and
sub-component
)
and every element is marked as part of one or both scopes
using the
data-scope
attribute:
<section data-scope="main-component">
<p data-scope="main-component">...<p>
<!-- sub-component root is in both scopes -->
<section data-scope="main-component sub-component">
<!-- children are only in the inner scope -->
<p data-scope="sub-component">...<p>
</section>
</section>
Those custom scope attributes are then
appended to every single selector in CSS:
p[data-scope~='main-component'] { color: red; }
p[data-scope~='sub-component'] { color: blue; }
/* both sections are part of the outer scope */
section[data-scope~='main-component'] { background: snow; }
/* the inner section is also part of the inner scope */
section[data-scope~='sub-component'] { color: ghostwhite; }
Using the ''@scope'' rule,
authors and tools can replicate similar behavior
with the unique attribute or class
applied only to the [=scoping roots=]:
<section data-scope="main-component">
<p>...<p>
<section data-scope="sub-component">
<p>...<p>
</section>
</section>
Then the class or attribute can be used
for establishing both upper and lower boundaries.
Elements matched by a lower boundary selector
are excluded from the resulting scope,
which allows authors to create non-overlapping scopes by default:
@scope ([data-scope='main-component']) to ([data-scope]) {
p { color: red; }
/* only the outer section is part of the outer scope */
section { background: snow; }
}
@scope ([data-scope='sub-component']) to ([data-scope]) {
p { color: blue; }
/* the inner section is only part of the inner scope */
section { color: ghostwhite; }
}
However, authors can use the child combinator
and universal selector to create scope boundaries that overlap,
such that the inner scope root is part of both scopes:
@scope ([data-scope='main-component']) to ([data-scope] > *) {
p { color: red; }
/* both sections are part of the outer scope */
section { background: snow; }
}
''@scope'' rules can be nested.
In this case, just as with the nested style rules,
the selectors of the inner ''@scope''
(including those defining its [=scope=])
are [=scoped selectors|scoped by=]
the selectors of the outer one.
Global, name-defining [=at-rules=]
such as ''@keyframes'' or ''@font-face'' or ''@layer''
that are defined inside ''@scope'' are valid,
but are not scoped or otherwise affected
by the enclosing ''@scope'' rule.
This means that style rules using the [=scoped descendant combinator=]
are sorted by specificity just like the regular [=descendant combinator=],
except that when their specificities are equal
the more tightly-scoped declaration wins.
In this example
the
<a>
element's color will be determined
by the nearest ancestor with either
a ''light-scheme'' or ''dark-scheme'' class.
(If the descendant selector had been used,
its color would always be ''plum'',
because it is later in the source order.)
.light-scheme >> a { color: darkmagenta; }
.dark-scheme >> a { color: plum; }
However if the
<a>
element has a ''light-scheme''
ancestor and is focused,
its color will be ''teal''
even if it has a nearer ''dark-scheme'' ancestor,
because there is no equivalent ''dark-scheme'' rule.
.light-scheme >> a:focus { color: teal; }