Large stylesheets eventually fight the cascade. A card component needs green image borders; a gallery elsewhere needs red ones. Without boundaries, you write .card img vs .gallery img, then .sidebar .card img when marketing drops a card inside the sidebar, then someone adds !important because a utility class won. CSS @scope — now Baseline Newly Available as of December 2025 with Firefox 146 joining Chrome 143+ and Safari 26.2+ — gives you an explicit subtree boundary evaluated before specificity. That is a different tool than cascade layers, and for component-scoped styling it is often the one you wanted.
What Baseline Status Means in 2026
Interop 2025 targeted @scope for cross-browser completion. web.dev’s December 2025 platform notes confirm all three major engines ship the at-rule in stable releases. Baseline Newly Available implies roughly 30 months until Widely Available — long enough to adopt in production for audiences on current evergreen browsers, short enough that you should stop treating scope as a progressive enhancement only.
Can I Use and the Web Features Explorer list Chrome 143, Firefox 146, Safari 26.2, and matching mobile versions as the support floor. If your analytics still show double-digit legacy mobile share, keep a fallback. For typical SaaS dashboards and marketing sites on evergreen traffic, @scope belongs in the default toolkit.
Syntax: Scope Root, Limit, and :scope
The minimal form sets a scope root selector:
@scope (.card) {
img {
border: 2px solid green;
}
}
Only img elements inside an element matching .card receive the rule — and specificity behaves as if the selector were scoped, not globally prefixed.
Add an upper boundary with to:
@scope (.card) to (.card__footer) {
a {
color: var(--link-color);
}
}
Links styled here apply from the .card root until the subtree hits .card__footer. That “donut scoping” pattern — style this region but not nested components beyond a cutoff — was previously impossible without duplicate wrapper classes or shadow DOM.
Inside @scope, the :scope pseudo-class refers to the scope root. Instead of repeating .card > .card__header, .card.card--featured > .card__header, write:
@scope (.card) {
:scope > .card__header {
font-weight: 600;
}
}
When @scope is nested in a <style scoped> block (where supported), the browser can infer roots from the element carrying the style — useful for single-file components, though explicit selectors remain clearer for shared stylesheets.
@scope vs. @layer vs. BEM
These solve different problems:
| Tool | Solves |
|---|---|
@layer | Priority ordering among entire rule sets (reset → base → components → utilities) |
@scope | Limiting which elements a selector can match inside a DOM subtree |
| BEM naming | Human-readable boundaries via class prefixes |
Our cascade layers guide covers layer ordering. Layers do not stop .sidebar .card img from matching if both selectors land in the same layer — specificity still decides. @scope prevents the match from happening outside the declared subtree regardless of specificity arms races.
BEM remains valuable for documentation and JavaScript hooks. You can shrink BEM depth when scope carries boundary enforcement: .card as the root class, inner elements named simply because @scope (.card) contains them.
Practical Patterns
Component variants without duplication.
@scope (.alert) {
:scope[data-variant="error"] {
background: var(--error-bg);
}
}
Third-party widget isolation. Scope rules to a wrapper #legacy-widget so inherited site typography does not leak in — and your global a { color: blue } does not leak out.
Card in sidebar without .sidebar .card. Scope the card; the sidebar’s layout grid never enters the selector.
Limit reach into nested web components. Pair @scope with :not() on the to boundary when slotted content should stay outside.
Migration Notes
Start new components with @scope. Retrofit hot spots where grep finds four-level descendant selectors or rising !important counts.
PostCSS and native bundlers pass @scope through unchanged — no plugin required on current toolchains. Lint rules may flag unknown at-rules until Stylelint configs update; disable the rule locally or upgrade @stylistic/stylelint-plugin configs.
Test donut boundaries in Safari and Firefox specifically; early implementations had edge cases around implicit scope roots in shadow trees. For flat DOM marketing pages, behavior matches the spec examples.
When Not to Use @scope
Shadow DOM still provides the strongest encapsulation when you control the component framework and need style secrecy from global sheets. @scope is lighter weight — no custom element registration, no slot projection model to teach the team.
Utility-first stacks that rely on single-class atoms everywhere may see less benefit; scope shines in component CSS files co-located with partial HTML.
The selector gymnastics era is not over — layers, container queries, and :has() still matter — but @scope removes an entire category of prefix chaining that existed only because the cascade had no native subtree boundary. Baseline status means you can write that boundary in CSS itself.



