A project combining a reset, a design system's component library, then page-specific tweaks almost always ends up fighting the same battle: a tweak written last, meant to win, loses against a slightly too specific design system selector. The usual fix stacks extra classes or adds an !important, which settles the current case while weakening the next one.
Cascade layers tackle the cause rather than the symptom: they let the declared order of style blocks decide, independently of the specificity inside each one.
Definition
A cascade layer, declared with @layer, groups CSS rules under a name. The order in which layers are first declared fixes their priority: a layer declared later always beats a layer declared earlier, regardless of the Specificity of the selectors inside each one.
@layer reset, components, utilities;
@layer reset {
a {
color: blue;
}
}
@layer components {
.card-link {
color: green;
}
}The first line declares the order without writing any rules yet: it is that line which fixes the final priority, even before the content of the layers appears in the file.
How layer order beats specificity
Inside a single layer, the normal rules of the Cascade and specificity apply as usual. Between two different layers, though, specificity stops mattering entirely: a highly specific selector in an earlier layer always loses against a plain selector in a later layer.
@layer reset, components;
@layer reset {
button.btn.btn-primary {
background: grey; /* very specific, but in the first layer */
}
}
@layer components {
.btn {
background: var(
--brand-color
); /* not very specific, but in the second layer: it wins */
}
}A use case: reset, design system and overrides without a fight
Three layers, declared in this order, solve the opening battle without a single selector needing to be strengthened: reset to zero out the browser's own styles, system for the design system's components, overrides for page-specific tweaks. Any rule in the last layer automatically wins against the two before it, even a single plain class against a design system selector loaded with three classes.
Assigning a layer to an import
The @import rule accepts a layer too, which makes it possible to file an entire third-party stylesheet into a specific layer without opening the file to wrap it manually inside an @layer block.
@layer reset, components;
@import "reset.css" layer(reset);
@import "button-library.css" layer(components);That syntax works just as well for a locally hosted stylesheet as for a third-party library loaded from a CDN, which settles in one line the case of an external design system whose CSS cannot be rewritten by hand.
@layer always beats layered CSS, regardless of layer order. When gradually migrating an existing project to layers, old unlayered code therefore keeps winning against carefully ordered new layers, which often surprises people the first time.Frequently asked questions
No, never, as long as neither layer uses !important: layer order is checked before specificity, which only settles ties between two rules inside the same layer.
Stronger, always. All CSS written outside of @layer behaves as if it belonged to an implicit final layer, placed after every named layer, which is why it keeps beating them.
Yes, the same layer name can be reopened as many times as needed, even across different files: the rules accumulate inside the layer, and only its position in the order declared at the start counts toward priority.