World’s largest virtual agentic engineering & quality conference
CSS @scope works in Chrome 118+, Edge 118+, Opera 106+, Safari 17.4+ on macOS and iOS, Samsung Internet 25+, and Firefox 146+. Learn cascade and quirks.

Prince Dewani
Author
Last Updated on: July 17, 2026
CSS @scope is a W3C cascade at-rule that confines a block of selectors to a DOM subtree, with an optional lower limit that creates a donut-shaped match region. It works in Chrome 118+, Edge 118+, Opera 106+, Samsung Internet 25+, Safari 17.4+ on macOS and iOS, and Firefox 146+, while Internet Explorer never supported it.
Note: As of December 2025, CSS @scope is officially part of Baseline 2025 (Newly Available), meaning it works across the latest versions of all major browsers.
This guide covers what CSS @scope is, the browsers that support it, donut scope, the cascade rules, feature detection, and known production issues.
Overview
CSS @scope is a W3C cascade at-rule that confines a block of selectors to a DOM subtree, with an optional lower limit that creates a donut-shaped match region. As of December 2025 it is part of Baseline 2025 (Newly Available).
Which browsers support CSS @scope?
What is donut scope?
Donut scope passes both a scope root and a scope limit to the @scope prelude, so the rules match only the elements that sit between the two boundaries and skip anything inside the limit.
How do you handle @scope across browsers?
Feature-detect with @supports and ship a fallback stylesheet for older browser tails. Because @scope cascade behavior differs slightly across engines, test on real browsers with TestMu AI's automation cloud.
CSS @scope is a W3C Cascade and Inheritance Level 6 at-rule that scopes a block of style rules to a chosen DOM subtree. You set a scope root selector for the upper bound and an optional scope limit for the lower bound, and the rules only match elements between them.
The formal syntax takes two parameters. The <scope-start> selector defines the scope root, the upper bound of the subtree, and is inclusive: the root element itself can be matched. The optional to <scope-end> selector defines the scope limit, the lower bound, and is exclusive: elements at or below the limit are not matched.
@scope (<scope-start>) to (<scope-end>) {
/* rulesets that only match between the root and the limit */
}
/* root only, no lower limit */
@scope (.article-body) {
img { border-radius: 8px; }
}
/* root plus limit: the donut */
@scope (.article-body) to (figure) {
img { border: 5px solid black; }
}Both parameters accept a full selector list, so a scope can have several roots or limits. Omitting the prelude entirely (a bare @scope block in an inline style element) scopes the rules to the enclosing element of that style block.
Every modern Chromium-based browser, Safari, and Firefox now supports CSS @scope by default. Firefox was the last major engine to ship it, and Internet Explorer never added the at-rule.
Chrome supports CSS @scope by default from Chrome 118 on Windows, macOS, Linux, ChromeOS, and Android. Chrome 104 to 117 had @scope hidden behind the Experimental Web Platform Features flag at chrome://flags. Chrome 4 to 103 did not support the at-rule at all.
Microsoft Edge supports CSS @scope from Edge 118 across Windows, macOS, Linux, Android, and iOS. Edge 104 to 117 hid it behind the same Experimental Web Platform Features flag at edge://flags. The legacy EdgeHTML browser does not support @scope and never will.
Firefox supports CSS @scope from Firefox 146 on Windows, macOS, Linux, and Android. Firefox 1 to 145 did not support the at-rule, so any older Firefox build silently ignores @scope blocks and falls through to the surrounding rules without any scoping applied.
Safari supports CSS @scope from Safari 17.4 on macOS Sonoma 14.4 and later, and from Safari 17.4 on iOS and iPadOS 17.4 and later. Safari 1 to 17.3 on macOS, plus iOS 1 to 17.3, do not support @scope. The Safari implementation matches the spec for scope roots, scope limits, and the proximity cascade rule.
Opera supports CSS @scope from Opera 106 on Windows, macOS, Linux, and Android. Opera 90 to 105 had @scope disabled by default behind the Experimental Web Platform Features flag at opera://flags. Opera Mobile supports @scope from Opera Mobile 80 on Android.
Samsung Internet supports CSS @scope from Samsung Internet 25 on Galaxy phones and tablets. Versions 4 to 24 did not support the at-rule. The implementation tracks Chromium, so the cascade and proximity rules behave the same as in Chrome for Android.
Chrome for Android supports CSS @scope from Chrome 118 on Android 8.0 and later. The legacy stock Android Browser, used on Android 4.4 and earlier, never added @scope. On modern Android devices, Chrome, Firefox for Android, and Samsung Internet all parse @scope by default.
Internet Explorer does not support CSS @scope in any version. Microsoft has retired Internet Explorer, and the @scope at-rule was specified long after IE 11 stopped getting feature work. Sites that still target IE need a CSS bundle that does not rely on @scope.
Note: CSS @scope behaves slightly differently across Chrome, Safari, and Firefox cascade engines. Test it on real browsers and OS with TestMu AI. Try TestMu AI free!
Donut scope is the most useful pattern @scope unlocks. You pass a scope root selector and a scope limit selector to the @scope prelude, and the rules only match elements that sit between the two boundaries. The donut shape comes from the gap: elements outside the root and elements inside the limit both fall out of the match set.
A typical donut scope looks like this:
@scope (.card) to (.card__content) {
img { border-color: green; }
}The rule turns every img green inside .card, but stops at .card__content. Images nested deeper than .card__content keep their default border. This is exactly what a component author wants when an inner slot embeds a child component with its own image styles. The donut keeps the parent component styling out of the inner slot, while still applying it to everything else inside the root.
@scope gives you selector isolation, not style isolation. Selector isolation means the selectors inside the block can only match elements between the scope root and the scope limit; they will never reach outside those boundaries.
Style isolation is a different guarantee, and @scope does not make it. Inherited properties such as color, font-family, and custom properties still cascade down through the scope limit like normal CSS inheritance. If a scoped rule sets color on a container, text inside the excluded donut hole still inherits that color, even though no selector in the scope can match it directly. Shadow DOM is the tool that provides true style isolation; @scope only controls where selectors apply.
Before @scope, teams got scoping from build-time tooling. CSS Modules (files named with the .module.css convention) and CSS-in-JS libraries both scope styles by rewriting class names into unique hashes at build time. Native @scope moves that job into the browser.
| Aspect | Native @scope | CSS Modules | CSS-in-JS |
|---|---|---|---|
| How it scopes | Browser limits selector matching to a DOM subtree at runtime. | Build step hashes class names per .module.css file. | Library generates unique, hashed class names from JS at build or runtime. |
| Tooling required | None, plain CSS. | Bundler support (preprocessor hashing). | A runtime or compile-time library. |
| Lower boundary (donut) | Yes, via the to (limit) clause. | No. | No. |
| Affects the cascade | Yes, adds scoping proximity. | No, normal specificity applies. | No, normal specificity applies. |
| Browser support | Baseline 2025; older browsers ignore it. | Works everywhere (output is plain CSS). | Works everywhere (output is plain CSS). |
@scope adds a new step to the cascade called scoping proximity. When two rules tie on specificity, the rule whose scope root sits closer to the matched element wins, no matter what order they appear in the source. Proximity sits between specificity and order of appearance in the cascade, so a less specific selector inside a closer scope can beat a more specific selector inside an outer scope.
The browser measures proximity by counting DOM hops: the number of levels up the DOM tree from the matched element to each scope root. If a paragraph sits one hop from a .light-theme root but two hops from a .dark-theme root, the light-theme rule wins the tie because its hop count is smaller.
The :scope pseudo-class targets the scope root itself and adds (0,1,0) of specificity. Selectors inside a scoped block get an implicit :scope prepended, so a bare img selector behaves like .card img inside @scope (.card). The & nesting selector inside @scope also points at the scope root, but it composes like a normal nested selector and can match the root from anywhere the selector applies, while :scope only matches the root once.
Use the @supports at-rule, the CSS.supports JavaScript API, or a quick DevTools probe to confirm the browser parses @scope before you ship a stylesheet that depends on it.
Paste this snippet into the browser DevTools console to confirm @scope support:
// Run in any browser DevTools console to confirm @scope support.
const scopeAtRule = CSS.supports("at-rule: @scope");
const scopePseudo = CSS.supports("selector(:scope)");
console.log("@scope at-rule parsed:", scopeAtRule ? "yes" : "no");
console.log(":scope pseudo-class parsed:", scopePseudo ? "yes" : "no");
if (!scopeAtRule) {
console.warn("This browser ignores @scope blocks. Ship a fallback stylesheet.");
}If the at-rule check prints no, your fallback stylesheet should ship the same rules under BEM-style class names so layout still holds on older browsers.
@scope reached baseline only after Firefox 146 shipped, so a long tail of older browsers still ignores the at-rule. The cascade changes also catch teams off guard when proximity flips a rule that worked under specificity alone.
In my experience, the most surprising failure happens on iOS 17.3. Safari parses the @scope keyword as an unknown at-rule and drops the whole block, including the rules that should fall through, so the page renders with no styles where the scoped rules used to apply. Always pair an @scope block with an @supports fallback when you target iPhone visitors.
All CSS @scope version numbers and platform notes in this guide come from these primary sources:
Author
Prince Dewani is a Community Contributor at TestMu AI specializing in AI agents, software testing, QA, and SEO. He is certified in Selenium, Cypress, Playwright, Appium, Automation Testing, and KaneAI, and presented academic research on AI agents at PBCON-01. At TestMu AI, he has also carried out extensive cross-browser research on the support of modern web technologies such as WebGPU, WebAssembly, WebXR, WebGL2 and other web technologies, validating their compatibility and feature parity across major browsers and rendering engines through rigorous hands-on testing. Prince has hands-on experience building AI agent workflows using Anthropic Claude, Google Antigravity, n8n, LangChain, and other agentic frameworks, and works regularly with MCP and A2A protocols. He shares his work with 5,500+ QA engineers, developers, DevOps experts, tech leaders, and AI agent practitioners on LinkedIn.
Did you find this page helpful?
More Related Blogs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance