Cascade Layers (@layer)
Cascade layers, introduced in 2022 via the @layer at-rule, give you explicit control over the cascade's priority ordering. Before layers, the only way to make one set of styles win over another was to increase specificity or use !important — both of which cause long-term maintainability problems. Layers let you define buckets of styles with a declared priority order, so you can say "component styles always win over base styles" without any specificity gymnastics.
The problem layers solve
Without layers, this is a common pain point when using a CSS framework or design system:
/* Bootstrap or Tailwind defines: */
.btn-primary {
background-color: #0d6efd !important;
color: #fff !important;
}
/* You try to override it in your own stylesheet: */
.btn-primary {
background-color: #0066cc; /* LOSES — library's !important wins */
}With layers, you can put the library in a lower-priority layer and your own styles in a higher one — no !important needed:
@layer framework {
.btn-primary {
background-color: #0d6efd;
color: #fff;
}
}
@layer components {
.btn-primary {
background-color: #0066cc; /* WINS — components layer is declared after framework */
}
}Declaring layers and setting their order
The order in which layers are declared determines their priority — later layers win. You can declare the ordering explicitly at the top of your stylesheet and then fill the layers in any order:
/* Declare the layer order upfront — one line, order matters */
@layer reset, base, layout, components, utilities;
/* Now fill them in — order of filling doesn't affect priority */
@layer components {
.card { border-radius: 8px; }
}
@layer base {
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
}
@layer reset {
* { margin: 0; padding: 0; }
}
@layer utilities {
.mt-4 { margin-top: 1rem !important; }
}
/* Priority: utilities > components > layout > base > reset
(last declared = highest priority in normal flow) */Unlayered styles always win
CSS that is not placed in any layer is considered "unlayered" and always has higher priority than any layered CSS — regardless of which layer is declared last. This is important to understand when mixing layered and unlayered styles:
@layer base {
p { color: navy; }
}
/* This rule is NOT in any layer — it always wins over layered styles */
p { color: crimson; }
/* Result: <p> is crimson, even though base layer is declared and the
unlayered rule has lower specificity (0,0,1) vs (0,0,1) — same! */Importing into a layer
/* Wrap an external stylesheet in a layer */
@import url('normalize.css') layer(reset);
@import url('bootstrap.css') layer(framework);
/* Declare the order after the imports */
@layer reset, framework, base, components, utilities;
/* Or combine declaration and import */
@layer framework {
@import url('bootstrap.css');
}
/* Note: @import must come before any other rules in the file
(except @layer order declarations and @charset) */Nested layers
Layers can be nested, creating a sub-hierarchy. Nested layer names use dot notation:
@layer components {
@layer buttons {
.btn { padding: 10px 20px; }
}
@layer cards {
.card { border-radius: 8px; }
}
}
/* Nested layers are accessible as: components.buttons, components.cards */
/* Nested layer priority — same rule: later declared wins */
@layer components {
@layer buttons, cards; /* buttons < cards within components */
}!important inside layers
The interaction between !important and layers follows the same reversal logic as !important with origin — the order flips:
@layer base, components;
@layer base {
p { color: navy !important; } /* !important in base layer */
}
@layer components {
p { color: crimson; } /* normal rule in components layer */
}
/* Result: navy !important from base wins over crimson from components.
!important flips the layer order — lower-priority layers win with !important */A real-world layer architecture
/* main.css — layer order declaration comes first */
@layer reset, base, tokens, layout, components, utilities;
/* Reset — lowest priority */
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
}
/* Design tokens */
@layer tokens {
:root {
--color-primary: #0066cc;
--spacing-base: 1rem;
--radius-md: 6px;
}
}
/* Base element defaults */
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
color: #222;
}
a { color: var(--color-primary); }
}
/* Component styles */
@layer components {
.card {
border-radius: var(--radius-md);
padding: var(--spacing-base);
}
.btn {
background: var(--color-primary);
color: white;
padding: 0.5em 1.5em;
}
}
/* Utilities — highest priority */
@layer utilities {
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0,0,0,0);
}
.mt-auto { margin-top: auto; }
}