Cascade layers

CSS · Advanced · 12 min

Layers end specificity wars between your code, frameworks and third-party CSS.

Cascade layers add a step to the cascade that's checked before specificity. Declare the order once:

@layer reset, base, components, utilities;

Then put rules into layers with @layer components { ... } or @import url(lib.css) layer(vendor). A declaration in a later layer beats one in an earlier layer, however specific the earlier selector is. So .mt-0 in utilities beats #sidebar .card h2 in components.

Rules:

  • Unlayered styles beat all layered styles (for normal declarations). Wrap third-party CSS in an early layer and it can never accidentally override yours.
  • Order is set by the first time a layer name appears, so declare the full order at the very top.
  • Layers can nest (@layer components.buttons).
  • !important reverses the order: an important declaration in an earlier layer beats one in a later layer, and important layered styles beat important unlayered ones. That lets a reset layer protect critical values.

Within a layer the normal rules still apply: specificity, then source order.

The revert-layer keyword rolls a property back to what the previous layers would have given it.

Layers suit design systems well: resets lowest, then base element styles, components, and utilities on top, so single-purpose classes always win without !important. Tailwind CSS v4 is built exactly this way.

Example

@layer reset, vendor, base, components, utilities;

@import url("datepicker.css") layer(vendor);   /* can't beat our layers */

@layer reset {
  *, *::before, *::after { box-sizing: border-box; margin: 0; }
}
@layer base {
  a { color: var(--link, royalblue); }
}
@layer components {
  #sidebar .card h2 { margin-block-end: 1rem; }  /* high specificity... */
}
@layer utilities {
  .mb-0 { margin-block-end: 0; }               /* ...still loses to this */
}

/* unlayered: beats every layer above */
.legacy-banner h2 { margin: 2rem; }

Remember

  • Layer order is checked before specificity
  • Declare the order up front: @layer a, b, c;
  • Unlayered styles beat layered ones
  • !important reverses layer order

Watch out for

  • Leaving most CSS unlayered and then wondering why layered framework styles never win.

Check yourself

  1. A rule in @layer base uses #id .a (high specificity); a rule in a later layer uses .a. Which wins?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@layer