@scope

Native CSS Scoping

A descendant selector like .card .title matches at any depth, which is exactly the problem the moment one .card contains another .card that reuses the same class names. @scope lets a rule apply only within a subtree, with an optional lower bound where it should stop looking, no BEM-style naming discipline or Shadow DOM required.

Command deck

.title styled by the outer rule

This heading should be tinted with the accent colour, whichever mode is on.

Cargo bay

.title reused, same class name

This one is a completely different component instance that just happens to share a class name with its ancestor.

The CSS

/* plain descendant selector: matches .title at ANY depth,
   including a .card nested inside another .card */
.card .title {
  color: var(--accent);
}

/* @scope, donut-scoped: styles .title inside the nearest .card,
   but stops before crossing into a .card nested inside that .card */
@scope (.card) to (.card) {
  .title {
    color: var(--accent);
  }
}

How it works

Both cards in the demo share the exact same .title class. Turn the toggle off and a plain .card .title descendant selector colours the outer heading, then keeps matching straight through into the nested card too, since nothing stops it there.

Turn it back on and the same visual rule is written as @scope (.card) to (.card): style the nearest .card’s subtree, but stop as soon as another .card shows up inside it. This “donut scope”, a scope with both an upper and a lower bound, is what makes reusable components safe to nest without every one needing a uniquely prefixed class name.

Chrome / Edge 118+, Safari 17.4+, Firefox 128+