Organising the cascade with @layer
CSS cascade layers let you split a stylesheet into explicitly ordered groups so that source order and specificity no longer decide everything. A rule in a later layer beats one in an earlier layer regardless of specificity — making resets, frameworks and overrides predictable. This reference covers the at-rule forms and the precedence rules.
How it works
Declare the layer order once with a statement-form @layer, then add rules with
the block form. Order is set by first appearance, so an upfront declaration is
the safest pattern:
@layer reset, base, components, utilities; /* fixes precedence */
@layer reset { * { margin: 0; } }
@layer base { a { color: blue; } }
@layer utilities { .text-red { color: red; } } /* wins over base */
@import url("framework.css") layer(base);
@import url("theme.css") layer(); /* anonymous layer */
For normal declarations a later layer wins; unlayered rules outrank all
layers. For !important declarations the order inverts — the earliest layer
wins — and important unlayered styles rank lowest.
The precedence table
This is the full precedence ranking for normal declarations, from lowest to highest:
- Rules in the first-declared layer (e.g.
reset) - Rules in the second-declared layer (e.g.
base) - Rules in the third-declared layer (e.g.
components) - Rules in the last-declared layer (e.g.
utilities) - Unlayered rules (highest normal precedence)
For !important declarations, this order completely inverts:
!importantunlayered (lowest among important)!importantin the last layer!importantin earlier layers!importantin the first-declared layer (highest among important)
This inversion is deliberate. It allows a base reset layer to enforce critical important rules (such as box-sizing) even if a component layer later adds its own important declarations.
Real-world layering pattern
A practical four-layer setup for a project that adopts a third-party design framework:
/* Declare order upfront, before any @import */
@layer reset, framework, components, utilities;
/* Place the framework in its designated layer */
@import url("tailwind.css") layer(framework);
/* Your components beat the framework regardless of specificity */
@layer components {
.card { padding: 1.5rem; }
}
/* Utilities beat everything else in normal declarations */
@layer utilities {
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; }
}
Any unlayered rule you write after this block beats even the utilities layer, which is a handy escape hatch for genuine one-off overrides without resorting to !important.
Tips and notes
- Declare the order upfront so import position cannot reshuffle layers.
- Unlayered styles always beat layered normal styles — keep ad-hoc overrides there.
- Nest layers with
@layer outer { @layer inner { ... } }orouter.inner. - Use
revert-layerto fall back to lower layers for one property.