CSS @scope Rule: Scoped Styles Without Frameworks

The CSS @scope at-rule (Chrome 118+, now in baseline) lets you write styles that only apply within a specific subtree of the DOM, without adding extra class names or relying on Shadow DOM. It solves the long-standing problem of component isolation directly in plain CSS.

Problem: CSS component styles bleed into unrelated elements — a .card a rule inside a plugin component overrides link colours in the site header because both share the global cascade.

Solution: Use the CSS @scope rule to limit a ruleset to a specific subtree: @scope (.card) { a { color: inherit; } }. The rule only matches a elements that are descendants of .card, with no need for wrapper selectors or BEM prefixes.


The examples below show the basic syntax, a donut scope (scoped styles that skip an inner region), and how to combine @scope with CSS custom properties for themed card components.


/* Basic @scope: styles only apply inside .card */
@scope (.card) {
    :scope {
        border: 1px solid var(--border, #ddd);
        border-radius: 8px;
        padding: 1.25rem;
    }

    h2 { font-size: 1.1rem; margin-block-end: .5rem; }
    p  { color: #555; line-height: 1.6; }
    a  { color: var(--accent, #0073aa); }
}

/* Donut scope: apply to .card but NOT inside .card-footer */
@scope (.card) to (.card-footer) {
    a { text-decoration: underline; }
}

/* Scoped custom-property theming */
@scope (.card--dark) {
    :scope {
        --border:  #444;
        --accent:  #7cb9e8;
        background: #1a1a1a;
        color: #eee;
    }
}

/* Lower specificity than a single class — great for design tokens */
@scope (.prose) {
    :scope { max-width: 65ch; }
    h2 + p { margin-block-start: .25rem; }
    blockquote {
        border-inline-start: 4px solid currentColor;
        padding-inline-start: 1rem;
        font-style: italic;
    }
}


<!-- Donut scope in practice -->
<div class="card">
    <h2>Title</h2>
    <p><a href="#">Body link — underlined by donut scope</a></p>
    <footer class="card-footer">
        <!-- link here is NOT underlined because footer ends the donut -->
        <a href="#">Footer link</a>
    </footer>
</div>


NOTE: @scope styles have lower specificity than the same selector written outside a scope block, which makes them ideal for design-system defaults that are easy to override locally.