Section 1 · The Cascade, for Real
Cascade Layers with @layer
18 min3 min readCode playground
Lesson at a glance
Organise a stylesheet into cascade layers so that whole groups of styles (reset, components, utilities) win by layer order instead of by specificity.
- 1Declare the order, then fill the layers
- 2Unlayered styles win
- 3Important declarations flip the order
- 4Nested layers and reverting
- 5Adopting layers in an existing codebase
- 6Your turn
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Waypoint’s stylesheet has grown four kinds of CSS: a reset, base element styles, components like the session card, and one-job utilities like .bg-accent that the content team sprinkles into the HTML. Utilities are supposed to win; that’s their whole point. But a component selector such as .schedule .session-card .badge scores (0,3,0), and a utility scores (0,1,0), so the utility loses and someone writes !important again.
Specificity was never designed to express “this group of styles outranks that group”. Cascade layers were.
Declare the order, then fill the layers
A layer is a named bucket of rules. You announce the order once, at the top of your CSS:
@layer reset, base, components, utilities;Then you put rules into layers, in any order, from any file:
@layer components {
.schedule .session-card .badge {
background: #ede9fe;
color: #5b21b6;
}
}
@layer utilities {
.bg-accent { background: #fde68a; }
}Now .bg-accent wins over the component’s background, despite its lower specificity. When the cascade reaches step 3 (layers) it sees the two declarations come from different layers and picks the later-declared one. It never gets to specificity. Inside a single layer, specificity and source order work as you already know.
The first @layer statement fixes the order. If you write @layer utilities { … } before ever declaring components, utilities becomes the first (lowest) layer. That’s why the order statement belongs at the very top of your entry stylesheet.
Unlayered styles win
Any normal rule that isn’t inside a layer goes into an implicit layer that beats all named layers. It feels backwards at first, but it’s what makes layers adoptable: you can wrap new code in layers today, and the existing unlayered CSS keeps its current behaviour.
The flip side is a classic trap.
Third-party files can go straight into a layer when you import them:
@layer vendor, reset, base, components, utilities;
@import url("calendar-widget.css") layer(vendor);Note that @import rules must come before other rules except @charset and @layer statements, which is exactly why the order statement can sit above it.
Important declarations flip the order
For !important declarations, layer priority reverses: an important rule in reset beats an important rule in utilities, and important layered rules beat important unlayered ones. This mirrors how origins work (a user’s important rule beats yours) and gives low layers a way to protect something essential. A reset can lock [hidden] { display: none !important; } and no component can accidentally show hidden content.
Nested layers and reverting
Layers can nest: @layer components.cards { … } creates a cards layer inside components, and its priority is resolved inside its parent. Teams use this to give each component folder its own sub-layer without affecting global order.
One more keyword pairs with layers: revert-layer. Setting color: revert-layer rolls the property back to whatever the previous layers would have produced, as if this layer’s declaration didn’t exist. It’s handy in a utility such as .reset-color { color: revert-layer; }.
Adopting layers in an existing codebase
You don’t need a rewrite. On the Waypoint codebase, the migration took one afternoon and three steps:
- Add the order statement at the very top of the entry stylesheet, including a
legacylayer first. - Wrap every existing file in
@layer legacy { … }, or import it withlayer(legacy). Nothing changes visually, because the old rules still compete with each other exactly as before. - As you touch a component, move its rules out of
legacyand intocomponents. Each move can only make your new code stronger relative to the old code, never weaker.
The pay-off arrives quickly: new components stop needing heavy selectors to beat old ones, and you can delete !important declarations one by one as their reason disappears.
Your turn
In the exercise, the Waypoint badge has a heavy component selector and a utility that should override it. Put the rules into layers so the utility wins without editing any selector or adding !important. Layers solve “which group wins”; the next lesson tackles “which value”, using custom properties as the dials your components expose.
Key takeaways
- Declare your layer order once at the top with
@layer reset, base, components, utilities;and later layers win over earlier ones regardless of specificity. - Specificity still matters, but only between declarations inside the same layer.
- Normal styles that are not in any layer beat every layered style, so wrap legacy or third-party CSS in a layer instead of leaving it loose.
!importantreverses layer order: an important declaration in an early layer beats an important one in a later layer.