Skip to content

Cascade Layers

CSS Cascade Layers (@layer) provide explicit control over the cascade, eliminating specificity wars and the overuse of !important.

/* Specificity battles */
.button {
background: blue;
}
/* Library styles - higher specificity to override */
.ui-library .button {
background: gray;
}
/* Our styles - even higher specificity */
body .content .ui-library .button {
background: blue;
}
/* Desperate times... */
.button {
background: blue !important;
}
/* Library fights back */
.ui-library .button {
background: gray !important;
}
/* Nuclear option */
.button.button.button {
background: blue !important;
}

Problems:

  • Specificity wars escalate quickly
  • !important makes maintenance difficult
  • Order of stylesheets matters unpredictably
  • Hard to override third-party styles
  • Refactoring is risky
/* Define layer order - earlier layers have lower priority */
@layer reset, base, components, utilities;
/* Reset styles - lowest priority */
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
/* Base styles */
@layer base {
button {
font: inherit;
cursor: pointer;
}
}
/* Component styles */
@layer components {
.button {
background: blue;
color: white;
padding: 8px 16px;
}
}
/* Utilities - highest priority */
@layer utilities {
.bg-red {
background: red;
}
}

Benefits:

  • Explicit, predictable cascade order
  • Lower specificity styles can override higher specificity
  • Easy to integrate third-party libraries
  • No need for !important
  • Clear architecture
/* Layers are prioritized by declaration order */
@layer one, two, three;
/* 'three' wins over 'two' wins over 'one' */
/* regardless of specificity within each layer */
@layer one {
.button.button.button {
color: red;
}
}
@layer three {
button {
color: blue; /* This wins! */
}
}
@layer base {
.button {
color: blue;
}
}
/* Unlayered styles have highest priority */
.button {
color: red; /* This wins */
}
@layer framework {
@layer base {
/* framework.base */
}
@layer components {
/* framework.components */
}
}
/* Reference nested layers */
@layer framework.components {
.button {
background: blue;
}
}
/* Define all layers upfront */
@layer reset, vendor, base, layout, components, utilities, overrides;
/* Reset */
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
}
/* Third-party libraries */
@layer vendor {
/* Import libraries into this layer */
}
/* Base typography and elements */
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.5;
}
a {
color: var(--color-primary);
}
}
/* Layout primitives */
@layer layout {
.container {
max-width: 1200px;
margin-inline: auto;
padding-inline: 1rem;
}
.grid {
display: grid;
gap: 1rem;
}
}
/* Components */
@layer components {
.button {
display: inline-flex;
padding: 0.5rem 1rem;
background: var(--color-primary);
color: white;
border-radius: 4px;
}
.card {
padding: 1rem;
border: 1px solid var(--color-border);
border-radius: 8px;
}
}
/* Utility classes - high priority */
@layer utilities {
.hidden {
display: none;
}
.flex {
display: flex;
}
.text-center {
text-align: center;
}
}
/* Emergency overrides */
@layer overrides {
/* Last resort fixes */
}
/* Import third-party CSS into a layer */
@import url('normalize.css') layer(reset);
@import url('some-library.css') layer(vendor);
/* Your styles automatically override vendor */
@layer components {
.library-button {
/* Overrides library styles easily */
background: blue;
}
}
@layer components {
.button {
padding: 8px 16px;
background: gray;
}
}
/* Variants in same layer - normal specificity rules apply */
@layer components {
.button-primary {
background: blue;
}
.button-large {
padding: 12px 24px;
}
}
@layer base, theme, components;
@layer base {
:root {
--color-primary: blue;
--color-text: black;
}
}
@layer theme {
/* Theme overrides base variables */
[data-theme='dark'] {
--color-primary: lightblue;
--color-text: white;
}
}
@layer components {
.button {
background: var(--color-primary);
color: var(--color-text);
}
}
/* Anonymous layers for one-off grouping */
@layer {
/* These styles are in an unnamed layer */
/* Cannot be added to later */
}
@layer base {
a {
color: blue;
text-decoration: underline;
}
}
@layer components {
.nav-link {
/* Revert to base layer styles */
color: revert-layer;
text-decoration: none;
}
}
@layer settings, /* Variables, config */
tools, /* Mixins, functions */
generic, /* Reset, normalize */
elements, /* Base HTML elements */
objects, /* Layout patterns */
components, /* UI components */
utilities; /* Helper classes */
@layer base, components, utilities;
@layer reset, vendor, theme, layout, components, utilities;

Supported in all modern browsers. See caniuse.com/css-cascade-layers.

  • No runtime performance impact
  • Layers are resolved at parse time
  • Reduces need for complex selectors
  • Smaller CSS files (no specificity hacks)

Use Cascade Layers when:

  • Building design systems or component libraries
  • Integrating third-party CSS frameworks
  • Managing large CSS codebases
  • Creating utility-first CSS architectures
  • You find yourself using !important frequently

Layer organization tips:

  • Define all layers upfront for predictability
  • Keep vendor/third-party in dedicated layers
  • Put utilities near the end for override capability
  • Document your layer architecture