A complex web document might include components such as headers, footers, news articles, maps, media players, ads, and others. As complexity increases, effectively managing the styling for these components becomes more of a concern, and effective scoping of styles helps us manage this complexity. Let's consider the following DOM tree:
body
└─ article.feature
├─ section.article-hero
│ ├─ h2
│ └─ img
│
├─ section.article-body
│ ├─ h3
│ ├─ p
│ ├─ img
│ ├─ p
│ └─ figure
│ ├─ img
│ └─ figcaption
│
└─ footer
├─ p
└─ img
If you wanted to select the <img>
element inside the <section>
with a class of article-body
, you could do the following:
- Write a selector like
.feature > .article-body > img
. However, that has high specificity so is hard to override, and is also tightly coupled to the DOM structure. If your markup structure changes in the future, you might need to rewrite your CSS. - Write something less specific like
.article-body img
. However, that will select all images inside the section
.
This is where @scope
is useful. It allows you to define a precise scope inside which your selectors are allowed to target elements. For example, you could solve the above problem using a standalone @scope
block like the following:
@scope (.article-body) to (figure) {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
The .article-body
scope root selector defines the upper bound of the DOM tree scope in which the ruleset will be applied, and the figure
scope limit selector defines the lower bound. As a result, only <img>
elements inside a <section>
with a class of article-body
, but not inside <figure>
elements, will be selected.
Note: This kind of scoping — with an upper and lower bound — is commonly referred to as a donut scope.
If you want to select all images inside a <section>
with a class of article-body
, you can omit the scope limit:
@scope (.article-body) {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
Or you could include your @scope
block inline inside a <style>
element, which in turn is inside the <section>
with a class of article-body
:
<section class="article-body">
<style>
@scope {
img {
border: 5px solid black;
background-color: goldenrod;
}
}
</style>
<!-- ... -->
</section>
Note: It is important to understand that, while @scope
allows you to isolate the application of selectors to specific DOM subtrees, it does not completely isolate the applied styles to within those subtrees. This is most noticeable with inheritance — properties that are inherited by children (for example color
or font-family
) will still be inherited, beyond any set scope limit.