CSS Cascade Layers (@layer) let you define explicit ordering for your stylesheet rules, solving specificity wars once and for all. Instead of fighting specificity with more specificity, you declare layers and the browser respects their order. Firefox 97+ supports cascade layers fully.
The Problem: Specificity Wars
Without layers, overriding third-party CSS or framework styles requires adding more specific selectors:
/* Bootstrap sets: */
.btn { background: blue; }
/* Your override needs higher specificity: */
.my-app .btn { background: green; } /* Hack: added parent class */
body .btn { background: green; } /* Another hack */
.btn.btn { background: green; } /* Double class hack */
This is the "specificity war" — you need increasingly awkward selectors to win.
@layer Basics
Define layers to control override priority:
/* Declare layer order upfront */
@layer reset, base, components, utilities;
/* Lower-priority layer first */
@layer reset {
* { box-sizing: border-box; margin: 0; padding: 0; }
}
@layer base {
body { font-family: system-ui; font-size: 16px; }
a { color: blue; }
}
@layer components {
.btn {
display: inline-flex;
padding: 8px 16px;
border-radius: 4px;
background: blue;
}
}
/* Higher-priority layer last */
@layer utilities {
.bg-red { background: red; }
.text-center { text-align: center; }
}
Key rule: later-declared layers win over earlier ones. utilities overrides components overrides base overrides reset.
And crucially: all layered styles lose to unlayered styles:
/* This wins over anything in any @layer */
.btn { background: green; }
Unlayered CSS is like an implicit top-priority layer.
Solving Third-Party Override Problems
Push third-party CSS into a low-priority layer:
/* Declare your app's layers first, then the third-party layer */
@layer reset, third-party, base, components, utilities;
/* Import Bootstrap into the low-priority layer */
@layer third-party {
@import url('bootstrap.css');
}
/* Your components automatically override Bootstrap, no specificity needed */
@layer components {
.btn { background: #3B82F6; } /* Just works! */
}
This replaces all Bootstrap specificity hacks with a single architectural decision.
Importing into Layers
/* Import a file into a layer */
@import 'reset.css' layer(reset);
@import 'third-party/bootstrap.css' layer(framework);
/* Or assign import layers inline */
@layer framework {
@import url('bootstrap.css');
}
Nested Layers
Layers can contain sub-layers:
@layer components {
@layer base, variants, states;
@layer base {
.btn { /* Base styles */ }
}
@layer variants {
.btn-primary { /* Primary variant */ }
.btn-danger { /* Danger variant */ }
}
@layer states {
.btn:hover { /* Hover state */ }
.btn:disabled { /* Disabled state */ }
}
}
/* Reference nested layers from outside */
@layer components.states {
.btn:focus { outline: 2px solid blue; }
}
Anonymous Layers
Layers without names — useful for one-shot CSS blocks that should have low priority:
@layer {
/* Anonymous layer — can't be referenced again */
.third-party-widget { color: blue; }
}
Anonymous layers are ordered in document order relative to named layers.
Practical Architecture: Design System Layers
/* Design system layer hierarchy */
@layer
reset, /* 1. Normalize browser defaults */
tokens, /* 2. CSS custom properties / design tokens */
base, /* 3. Element defaults (h1, p, a, etc.) */
layout, /* 4. Page structure (grid, containers) */
components, /* 5. UI components (btn, card, modal) */
utilities; /* 6. Single-purpose utility classes */
/* App-specific overrides outside layers (highest priority) */
Layers and Specificity
Within a layer, specificity still matters:
@layer base {
a { color: blue; } /* Less specific */
nav a { color: inherit; } /* More specific — wins WITHIN this layer */
}
But cross-layer: later layer always wins regardless of specificity:
@layer first {
body main section .container a.link { color: blue; } /* Very specific! */
}
@layer second {
a { color: red; } /* Wins — later layer always beats earlier layer */
}
This is the superpower of cascade layers.
Firefox DevTools: Viewing Layers
Firefox DevTools shows cascade layers in the Inspector:
- Open Inspector → click an element
- In Styles panel: layers are shown with their layer name (e.g.,
components) - The layer order affects which styles are overriding which
CSS Cascade Layers are a foundational improvement for CSS architecture — especially in large codebases with multiple teams or third-party integrations.
Top comments (0)