Cascade Layers
Specificity Wars Are Over
Before cascade layers, controlling CSS priority meant managing specificity — adding classes, avoiding IDs, using BEM, fighting !important chains. Layers solve this fundamentally: you declare priority at the architectural level, and specificity becomes irrelevant between layers.
The declaration order of @layer determines priority, not the specificity of selectors within those layers. A single class selector in a higher-priority layer beats an ID+class+element selector in a lower-priority layer.
Think of cascade layers as floors in a building. The ground floor (first declared layer) has the lowest priority. The top floor (last declared layer) has the highest. Specificity battles only happen within the same floor. A resident on the 10th floor always wins over a resident on the 1st floor, regardless of which apartment number (specificity) they have.
Declaring and Using Layers
Basic Layer Declaration
/* Declare layer order up front */
@layer reset, base, components, utilities;
/* Fill layers anywhere in your CSS */
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
}
}
@layer base {
body { font-family: system-ui; color: var(--text); }
a { color: var(--link); }
}
@layer components {
.card { padding: 1.5rem; border-radius: 8px; }
.btn { padding: 0.75rem 1.5rem; }
}
@layer utilities {
.text-center { text-align: center; }
.mt-4 { margin-top: 1rem; }
}
Priority order: utilities > components > base > reset
Importing Into Layers
/* Put third-party CSS into a layer at import time */
@import 'normalize.css' layer(reset);
@import 'component-library.css' layer(vendor);
/* Declare the full order */
@layer reset, vendor, base, components, utilities;
Anonymous Layers
/* Unnamed layer — can't be appended to later */
@layer {
.temporary-fix { display: none; }
}
Nested Layers
@layer framework {
@layer base {
/* framework.base — lowest in framework */
}
@layer components {
/* framework.components — higher in framework */
}
}
/* Reference nested layers with dot notation */
@layer framework.base {
/* Append to the nested layer */
}
Layer Priority Rules
Declared Order = Priority
@layer A, B, C;
/* Priority: C > B > A (last declared wins) */
Unlayered Beats All Layers
@layer base { .btn { color: blue; } }
/* Unlayered — highest priority among normal author styles */
.btn { color: red; } /* This wins */
This means if you layer YOUR code but leave third-party code unlayered, the third-party code wins. The intended pattern is the reverse: put third-party code in layers (to contain it) and keep your overrides unlayered (or in higher-priority layers).
!important Reverses Layer Order
Normal declarations: last layer wins.
!important declarations: first layer wins (priority reverses).
@layer reset, base, utilities;
@layer reset {
a { color: blue !important; } /* Wins among !important — it's in the first layer */
}
@layer utilities {
.text-red { color: red !important; } /* Loses to reset's !important */
}
This ensures foundational layers (resets, accessibility) can protect their !important rules from being overridden by higher layers.
Why !important reverses layer order
The cascade's design principle is that lower-origin declarations can protect their critical rules. For normal declarations, higher layers override lower ones. But for !important, the intent is to protect — so lower layers' !important rules get higher priority. This mirrors how user !important beats author !important at the origin level.
Production Architecture with Layers
Recommended Layer Stack
@layer reset, vendor, tokens, base, layouts, components, features, utilities;
@layer reset {
/* Box-sizing, margin reset, minimal normalization */
}
@layer vendor {
/* Third-party libraries — contained at low priority */
@import 'headless-ui.css';
@import 'syntax-highlighter.css';
}
@layer tokens {
/* Design tokens as custom properties */
:root { --color-primary: oklch(0.55 0.19 240); }
}
@layer base {
/* Element defaults: body, headings, links, lists, tables */
}
@layer layouts {
/* Page-level layouts: sidebar, grid, holy grail */
}
@layer components {
/* Reusable components: cards, buttons, modals, forms */
}
@layer features {
/* Feature-specific styles that override components */
}
@layer utilities {
/* Utility classes: spacing, alignment, visibility */
}
/* Unlayered: emergency overrides, truly exceptional cases */
Migrating an Existing Codebase
/* Step 1: Wrap existing CSS in a single layer */
@layer legacy {
@import 'existing-styles.css';
}
/* Step 2: Write new CSS in structured layers above legacy */
@layer legacy, base, components, utilities;
/* Step 3: Gradually move rules from legacy to proper layers */
/* Step 4: Remove empty legacy layer when migration is complete */
| What developers do | What they should do |
|---|---|
| Putting your application code in a layer while leaving third-party code unlayered Unlayered styles beat all layered styles. Third-party code should be contained, not elevated. | Put third-party code in layers. Keep your highest-priority code unlayered or in the highest layer. |
| Expecting !important in a high-priority layer to beat !important in a low-priority layer The cascade protects lower layers' !important rules from being overridden by higher layers | !important REVERSES layer order — first declared layer's !important wins |
| Using anonymous layers for code you might need to extend later Anonymous layers can't be reopened or appended to from elsewhere in the codebase | Always name your layers unless the code is truly one-off and temporary |
| Declaring @layer order in multiple places Multiple @layer declarations create layers in the order they're first encountered. Scattered declarations make priority hard to reason about. | Declare the complete @layer order once at the top of your entry CSS file |
- 1Last-declared layer has highest priority for normal declarations
- 2!important REVERSES layer order — first-declared layer's !important wins
- 3Unlayered styles beat all layered styles — use this to your advantage
- 4Contain third-party CSS in layers. Keep your overrides unlayered or in the highest layer.
- 5Declare the complete @layer order once at the top of your entry CSS file