CSS: @scope at-rule

By Xah Lee. Date: . Last updated: .

What is @scope at-rule

CSS @scope at-rule lets you target styles only to a selector (and its descendants), such that the selector is locally scoped, has no effect or inteference with global selectors. In other words, it creates a scoped style set.

quick example:

/* this means, the style only apply to class x and its descendants. */
/* inside the parenthesis, it can be any css selector */
@scope (.x) {
 p {
  color: red;
 }
}

Inline scoped style anywhere in HTML!

another greate major feature is that scope at-rule lets you place HTML style tag anywhere in HTML such that it has effect only at that container in dom tree.

How is scope different from using descendant selector?

/* how is the following two different */

@scope (.x) { p { color: red; } }

/* vs */

.x p { color: red; }

Scope is much easier to manage

Using scope is much easier to manage. You can group many rules together, you don't have to create a descendant selector for each rule.

Scope does not add specificity

Scope does not add specificity. but descendant selector does.

with descendant selector you have to worry about clashing with other selectors.

Scope create a local CSS scope. Descendant selector is in global scope.

Scope resolves style clash by nearest DOM tree ancestor

when two scopes clash, namely, two scopes target the same html element with same CSS property, it is resolved by the element's nearest ancestor.

For example, if you have this:

/* this scope has scope root .x */
@scope (.x) { span { color: blue; } }

/* this scope has scope root p */
@scope (p) { span { color: red; } }
<div class="x">
<p><span>what color am i</span></p>
</div>

it determines which rule to apply to the span by comparing the distances of the scope roots to the span. Nearest wins. Distance is defined as number of dom tree nodes hop. In this case, scope root p is closer, therefore, that span is colored red.

Basic syntax

@scope (scope-root) { rules }

scope-root can be a selector list (,).

scope-root itself is part of the scope. but to change style on it, you need to use the pseudo-class selector :scope.

@scope (.x) {
 /* make the .x itself red */
 :scope {
  color: red;
 }
}

With scope-limit (scope with donut hole)

Also allows a second selector to filter out the dom tree bottom branches. This is often called Donut Scope.

@scope (scope-root) to (scope-limit) { rules }

scope-limit itself is not part of the scope.

For example

/* scope, from class x to class y.
class y is not part of the scope. */
@scope (.x) to (.y) {
 p {
  color: red;
 }
}
<div class="x">
 <p>this is red</p>
 <p class="y">not red</p>
</div>

multiple donut holes

🛑 WARNING: the selector in scope-limit may match multiple elements, so it creates multiple holes. also, it can be a selector list (,). Think of it as filtering out branches of the DOM tree in the scope.

The pseudo-class :scope

:scope refers to the scope-root element.

if you want to modify the scope-root's style, you need to use it.

@scope (.x) {
 /* make the .x itself red */
 :scope {
  color: red;
 }
}

:scope can be used in scope-limit:

/* anything inside section tag, but exclude any direct-child figure tag. */
@scope (section) to (:scope > figure) {
 img {
  max-width: 90%;
 }
}

The nesting selector &

when the nesting selector & is used inside scope, it refers to the scope root.

Inline @scope in HTML style tag

Scope proximity in cascade

Advanced tips

Inclusive/Exclusive Bounds

you can use child combinators to in a sense “exclude” the scope-root elements. or, use it to “include” the scope-limit elements. For example,

@scope (.root > *) to (.limit > *)

Nesting @scope

scope can be nested.

Scope and inheritance

@scope limits selectors, not styles. Inherited properties (e.g. color) can leak beyond the lower bound.

Global At-Rules

@keyframes, @font-face, @layer inside @scope remain global.

Invalid Selections

You cannot use a selector inside scope such that it goes outside of the scope (e.g. :scope + p is invalid, because it selects the next sibling after scope.).

Browser support

supported by all major browsers since 2026.