CSS Cascade Layers (@layer)
Organize CSS specificity with @layer to create predictable, manageable style hierarchies free from specificity wars.
Overview
Key Points
- •Declare layer order with @layer name1, name2, name3 before any layer rules
- •Later layers have higher priority than earlier layers
- •Unlayered CSS sits above all layers in the cascade
- •Layers created inside @import keep third-party CSS sandboxed in your layer stack
- •Nested layers use dot notation: @layer framework.base, framework.components
- •You can add to a layer multiple times across your stylesheet
- •Within a layer, normal specificity rules still apply
- •@layer is supported in Chrome 99+, Firefox 97+, Safari 15.4+
Examples
Basic layer setup for a design system
/* Step 1: Declare order at the top of your main CSS file */
@layer reset, tokens, base, components, utilities;
/* Step 2: Populate each layer */
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
}
@layer tokens {
:root {
--color-primary: #6366f1;
--color-text: #0f172a;
--radius-md: 8px;
--space-4: 16px;
}
}
@layer components {
.card {
background: white;
border-radius: var(--radius-md);
padding: var(--space-4);
}
}
@layer utilities {
/* Even with low specificity, utilities win over components */
.p-0 { padding: 0; }
.hidden { display: none; }
}Utilities always win over components because they are in a later layer, not because of higher specificity.
Sandboxing third-party CSS with @import layers
/* Put third-party styles in early layers so your code overrides them */
@import url("normalize.css") layer(reset);
@import url("bootstrap.css") layer(vendor);
/* Declare your layers after (they are higher priority) */
@layer reset, vendor, base, site;
@layer site {
/* These rules beat Bootstrap even at low specificity */
.card {
border-radius: 12px;
border: none;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
}This pattern is the primary motivation for @layer: wrapping third-party CSS so your overrides are always predictable.
Nested layers for library authors
/* Framework ships its own layer hierarchy */
@layer ui {
@layer reset {
* { box-sizing: border-box; }
}
@layer base {
body { font-family: system-ui; }
}
@layer components {
.btn { padding: 8px 16px; }
}
}
/* Consumers can override the nested layers */
@layer ui.components {
/* Adding to the nested layer from outside the framework */
.btn { border-radius: 99px; }
}
/* Or override everything by writing outside ui entirely */
.btn { background: red; } /* unlayered, wins over all ui layers */Revert-layer for graceful fallbacks
@layer base, overrides;
@layer base {
.btn {
background: #6366f1;
color: white;
padding: 8px 16px;
border-radius: 6px;
border: none;
cursor: pointer;
}
}
@layer overrides {
.btn-ghost {
/* Revert all properties to what they would be without the overrides layer */
background: revert-layer;
color: #6366f1;
border: 2px solid currentColor;
}
}revert-layer rolls back properties to the value from the previous layer, not the browser default.
Common Mistakes
- Adding unlayered rules that unintentionally beat all layers (unlayered always wins)
- Declaring a @layer after rules have already been added to it silently reorders nothing (order is set by the first declaration)
- Assuming later specificity beats layers (a .btn rule in an earlier layer loses to a * rule in a later layer)
- Forgetting that !important reverses layer order (important rules in earlier layers win over later layers)
Note
A practical layer stack for most projects: @layer reset, theme, base, components, utilities. Put third-party imports in reset or a vendor layer, your design tokens in theme, element defaults in base, your UI components in components, and Tailwind-style helpers in utilities.
Exercises
Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.
Completed: 0 / 3 exercises
Knowledge Check
Question 1 of 4