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
May 6, 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.
This guide covers what CSS @scope is, the browsers that support it, donut scope, the cascade rules, feature detection, and known production issues.
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.
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 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 :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:
Did you find this page helpful?
More Related Hubs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance