// css · Web Platform Advent #19
CSS Cascade Layers: how @layer makes specificity negotiable
Cascade layers let you decide which rules win by ordering layers instead of escalating selectors. How @layer works, and the two places where the priority order inverts.
Most CSS specificity problems are not really about specificity. They are about two stylesheets disagreeing, and the only tool available being to make one selector heavier than the other. Cascade layers replace that arms race with an explicit order.
What a layer actually changes
MDN puts the benefit plainly: rules within a cascade layer cascade together, giving more control over the cascade to web developers. The consequence is the interesting part, because it removes a constraint you have probably worked around for years.
You can use simpler selectors, because you no longer have to ensure a selector has high enough specificity to override a competing rule. All you need to ensure is that it appears in a later layer.
The mechanism is blunt: once the layer order has been established, specificity and order of appearance are ignored between layers. A rule in a later layer applies even if it has lower specificity than the rule it overrides. Inside a single layer, normal specificity rules still decide between competing rules, so nothing you know is wasted, it just stops applying across layer boundaries.
Declaring the order up front
The order is set by the order of declaration: the first declared layer gets the lowest priority, and the last declared layer gets the highest.
That makes the statement form the one worth reaching for, because it fixes the whole order in a single line before any rule exists:
@layer theme, layout, utilities; From then on, anything in utilities beats anything in layout, which beats anything in theme, regardless of how the selectors are written.
One detail makes this robust in a real codebase. Having declared the names, you add rules to a layer by re-declaring its name, and the styles are appended to that layer without changing the order. So you can spread a layer across many files, imported in any sequence, and the priority you decided in that first line still holds.
The first inversion: unlayered styles win
Here is where intuition fails most people, and it is worth stating in the strongest terms MDN uses.
Styles that are not defined in a layer always override styles declared in named and anonymous layers.
Read that against the natural assumption. You would expect layers to be a hierarchy that ordinary CSS joins at the bottom. It is the opposite: unlayered CSS sits above every layer you declared, for normal declarations.
This is not an accident, and it is genuinely useful once you see the shape of it. Put a third-party stylesheet or a reset into a layer and your own unlayered rules override it without a single specificity fight. The trap is only for the person who layers everything except one forgotten file, then cannot work out why that file always wins.
The second inversion: !important flips the whole thing
The other surprise is that the priority order among important rules is the inverse of normal rules.
MDN spells out both halves. Within author styles, all important declarations inside CSS layers take precedence over important declarations outside a layer. And all normal declarations inside layers have lower priority than declarations outside a layer.
So the same code base has two orders running in opposite directions at once. For normal declarations, the last layer wins and unlayered CSS wins over everything. For !important declarations, the first declared layer wins, and layered styles beat unlayered ones.
Practically, this means a reset placed in your first layer can make a declaration genuinely unoverridable by marking it important, which is a real capability and a real footgun. It also means that debugging a cascade issue requires knowing whether the declaration involved is important before reasoning about layer order at all.
How to use it without surprises
Three habits cover most of it.
Declare the full order in one line, early. A single @layer statement at the top of your entry stylesheet is the whole configuration. Anyone reading it knows what beats what without opening another file.
Decide deliberately what stays unlayered. Since unlayered CSS outranks every layer for normal declarations, leaving styles outside a layer is a choice with consequences, not a neutral default. Either put everything in a layer, or keep the unlayered set small and intentional.
Treat important declarations as a separate system. They do not follow the order you designed, they follow its mirror image. If you find yourself reaching for !important inside a layered architecture, the layer order is usually the thing that needed adjusting.
Cascade layers do not make CSS simpler to learn, they add a mechanism with two counterintuitive inversions. What they do is make the outcome of a conflict something you decided in advance rather than something you discovered by counting selectors.