CSS @scope Rule: Creating Scoped Style Blocks
The CSS @scope rule is a native styling feature designed
to isolate CSS rules to specific subtrees of the Document Object Model
(DOM). This article explains the fundamental purpose of
@scope, how it eliminates the need for strict naming
methodologies or the heavy overhead of Shadow DOM, and how features like
"doughnut scoping" provide precise control over where styles begin and
end.
The Purpose of @scope
Traditionally, CSS operates in a global scope. To prevent styles applied to one component from unintentionally leaking into another, developers have historically relied on class-naming conventions like BEM (Block Element Modifier), CSS Modules, or CSS-in-JS libraries. While the Shadow DOM provides true encapsulation, it introduces significant complexity regarding accessibility, global styles, and script interactions.
The primary purpose of the @scope at-rule is to
establish a native, declarative mechanism for defining scoping
boundaries directly within standard CSS. It ensures that rules only
match elements located within a specific DOM branch.
Defining a Scoping Root
The simplest use of @scope designates a scoping root.
When styles are placed inside this block, they only apply to descendants
of the defined root element.
@scope (.card) {
h2 {
color: darkblue;
}
p {
font-size: 0.95rem;
}
}In this example, the h2 and p selectors
will only match elements inside an element with the .card
class. Any headings or paragraphs outside of .card remain
unaffected, without requiring compound selectors like
.card h2.
Establishing Scoping Limits ("Doughnut Scoping")
One of the most powerful capabilities introduced by
@scope is the ability to define a scoping limit using the
to syntax. This pattern, often referred to as "doughnut
scoping," styles an element's container while leaving nested child
components untouched.
@scope (.media-card) to (.media-card-content) {
img {
border-radius: 8px;
}
}Here, the img rule applies only to images inside
.media-card, but stops applying as soon as it reaches
.media-card-content. This prevents parent component styles
from accidentally bleeding into nested child components.
Targeting the Scoping
Root with :scope
Inside a @scope block, the :scope
pseudo-class allows developers to target the scoping root element
itself:
@scope (.alert) {
:scope {
border: 1px solid red;
padding: 1rem;
}
}This syntax provides a clean way to bundle both the container's styles and its internal element styles into a single, cohesive block.
Specificity and Precedence
The @scope rule also impacts CSS specificity in an
intuitive way. Selectors inside a scoped block inherit the specificity
of the scoping root selector in addition to their own specificity.
Furthermore, when two scoped rules compete with identical specificity,
the rule associated with the "closer" scoping root in the DOM tree wins.
This resolves the long-standing CSS issue of nested component
proximity.
By eliminating naming conventions, reducing selector specificity
bloat, and offering boundary-defined encapsulation, the
@scope rule allows developers to write modular,
predictable, and maintainable styles directly in modern CSS.