A selector meant for one component can accidentally style a similar element elsewhere—or inside a nested component. CSS scope uses the @scope at-rule to restrict a group of rules to a defined part of the document. This gives modular CSS a browser-enforced boundary instead of relying only on naming conventions and developer discipline. section 9.3 section 9.1
Write @scope (.media) { … } to start a scope at an element matching .media. Rules inside can use short selectors such as img or h4 for elements in that scope. The :scope pseudo-class selects the root itself; :scope.right selects that root when it also has the right class. section 9.3
Listing 9.14 The media module written with scope
@layer modules {
@scope (.media) { #1
:scope { #2
display: flex;
gap: 1.5em;
padding: 1.5rem;
background-color: #eee;
border-radius: 5px;
}
:scope.right {
flex-direction: row-reverse;
}
img { #3
align-self: start;
}
.body { #3
overflow: auto;
margin-block-start: 0;
}
h4 { #3
margin-block-start: 0;
color: #333;
}
}
}
The media module keeps its root, image, body, and heading rules together inside one scope, without requiring BEM-style names for every child. section 9.3
A scope is not an automatic wall around nested modules. If an outer and inner scope both style the same property on an element, selector specificity still wins first. When specificity ties, the nearest scope wins before source order. Thus a nested media module’s h4 color can beat an outer highlight block’s h4 color—but an outer rule for a property the media module does not set can still apply. section 9.3.1 section 9.3.2
Listing 9.18 Adding a scoping limit
@layer modules {
@scope (.highlight-block) to (.slot) {
:scope {
padding: 1rem 1.5rem;
background-color: #b3cbe6;
}
h1, #1
h2, #1
h3, #1
h4 { #1
color: #264b73;
}
}
}
The to (.slot) limit stops the highlight block’s scoped heading rules at a designated inner boundary, preventing them from reaching into the nested module. section 9.3.2
Use @scope (.highlight-block) to (.slot) when a container is expected to hold other modules. The to selector supplies an inner limit: preventing unwanted matches is more reliable than resolving conflicts afterward. For deliberate interaction between modules, choose specificity and cascade layers instead. A higher-priority layer can override a lower-priority one independently of scope proximity. section 9.3.2 section 9.3.4
One-off content
A <style> element inside a wrapper can contain @scope { .message { … } }. With no selector after @scope, the parent wrapper becomes the implicit root: its message is styled, while a matching message outside it is not. This suits an embedded demo better than a reusable module whose CSS would otherwise need repeating. section 9.3.3
Use scope to keep reusable component rules near their intended elements, and add a scoping limit when a component must not style its descendants’ modules. Check browser support before depending on it: the cited rollout began with Chrome 118 in October 2023, with other implementations still emerging at that point. section 9.5 section 9.3
