Articles /
@layer: cascade layers from scratch
How @layer gives you explicit control over the cascade — no more specificity wars.
The specificity problem @layer solves
Before cascade layers, CSS specificity was a numbers game. A utility class like .text-center (specificity 0,1,0) loses to a component selector like .card .title (0,2,0). The usual fix was !important, which then created its own priority battles. Developers invented BEM, ITCSS, and utility-first frameworks to work around a problem that was fundamentally architectural.
@layer solves this at the language level. You declare named layers, and CSS respects their order regardless of selector specificity. A rule in a later layer always beats a rule in an earlier layer.
How @layer works
Declare your layer order up front, then put CSS into those layers:
/* Declare order — first listed = lowest priority */
@layer reset, base, components, utilities;
/* Add rules to layers */
@layer reset {
*, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer base {
body { font-family: system-ui; color: oklch(0.18 0.025 260); }
a { color: oklch(0.52 0.22 265); }
}
@layer components {
.card .title { font-size: 1.1rem; font-weight: 700; }
.btn { padding: 0.5rem 1rem; border-radius: 0.5rem; }
}
@layer utilities {
.text-center { text-align: center; }
.hidden { display: none; }
}
Even though .card .title has higher specificity than .text-center, a rule in the utilities layer wins because it's declared later. The cascade now follows your architecture, not your selector complexity.
Unlayered CSS and !important
CSS that lives outside any @layer has higher priority than all layered CSS. This means you can adopt layers incrementally — your existing stylesheets still win over anything you put in a layer. The flip side: !important rules in layers have reversed priority. An !important in the first layer beats !important in the last layer. This is intentional — it lets your reset layer use !important defensively without being overridden by component layers.
Practical layer architecture
A proven five-layer stack for production projects:
@layer reset, vendor, base, components, utilities;
@layer vendor {
/* Third-party CSS goes here — always overridable by your code */
}
@layer base {
/* Design tokens, typography, global defaults */
:root { --accent: oklch(0.52 0.22 265); }
}
@layer components {
/* All component styles — cards, buttons, nav, forms */
}
@layer utilities {
/* Single-purpose overrides — always win */
}
Put third-party CSS (a date picker, a rich-text editor) in the vendor layer and you can always override it from components or utilities without specificity hacks.
Browser support and migration
@layer is supported in Chrome 99+, Safari 15.4+, and Firefox 97+. In 2026, it's Baseline Widely Available. You can adopt it incrementally: wrap your reset in a layer first, then components, then utilities. Unlayered CSS continues to work at the highest priority, so nothing breaks during migration.
@layer reset, base, components, utilities; at the top of your main stylesheet and wrapping your reset. You'll see immediate benefits in specificity management.@layer and other modern CSS
Layers compose with @scope and native nesting. A @scope block inside a layer respects both the scope boundary and the layer priority. Nested selectors inherit their parent's layer. This combination — layers for priority, scope for containment, nesting for readability — is the modern CSS architecture stack.