CSSCascade Layers (@layer)

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:

CSS
/* 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:

CSS
@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:

CSS
/* 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) */
Declare your layer order at the very top of your entry stylesheet — this gives you a clear map of your CSS architecture at a glance
The layer declaration `@layer reset, base, layout, components, utilities;` is a single statement that documents your entire priority structure. Anyone reading the stylesheet knows immediately that utility styles win over component styles, which win over base styles. This clarity is one of layers' greatest benefits, separate from the technical specificity control.
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:

CSS
@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! */
Unlayered CSS always beats layered CSS regardless of specificity or order — if you mix layered and unlayered styles, unlayered wins
This means that third-party CSS you import without wrapping in a `@layer` will beat all your layered styles. Always wrap third-party imports in a layer: `@layer framework { @import 'bootstrap.css'; }`. If you're migrating an existing codebase to layers, put your existing CSS in a layer too — otherwise the unlayered rule still wins and layers don't help you.
Importing into a layer

CSS
/* 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:

CSS
@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:

CSS
@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 */
!important reverses layer priority the same way it reverses origin priority — a rule in an earlier (lower-priority) layer with !important beats a later layer's normal rule
This reversal is consistent with the rest of the cascade design. Just as user `!important` overrides author `!important` to protect accessibility, an early layer's `!important` beats a later layer's normal rule. In practice, avoid `!important` inside layers — the whole point of layers is to make `!important` unnecessary.
A real-world layer architecture

CSS
/* 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; }
}
Next
Understand the physical space every element occupies — the foundation of all layout: [The Box Model](/css/box-model).