DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

CSS Cascade Layers (@layer): End Specificity Wars Forever

CSS Cascade Layers (@layer) let you define explicit ordering for your stylesheet rules, solving specificity wars once and for all. Instead of fighting specificity with more specificity, you declare layers and the browser respects their order. Firefox 97+ supports cascade layers fully.

The Problem: Specificity Wars

Without layers, overriding third-party CSS or framework styles requires adding more specific selectors:

/* Bootstrap sets: */
.btn { background: blue; }

/* Your override needs higher specificity: */
.my-app .btn { background: green; } /* Hack: added parent class */
body .btn { background: green; }    /* Another hack */
.btn.btn { background: green; }     /* Double class hack */
Enter fullscreen mode Exit fullscreen mode

This is the "specificity war" — you need increasingly awkward selectors to win.

@layer Basics

Define layers to control override priority:

/* Declare layer order upfront */
@layer reset, base, components, utilities;

/* Lower-priority layer first */
@layer reset {
  * { box-sizing: border-box; margin: 0; padding: 0; }
}

@layer base {
  body { font-family: system-ui; font-size: 16px; }
  a { color: blue; }
}

@layer components {
  .btn {
    display: inline-flex;
    padding: 8px 16px;
    border-radius: 4px;
    background: blue;
  }
}

/* Higher-priority layer last */
@layer utilities {
  .bg-red { background: red; }
  .text-center { text-align: center; }
}
Enter fullscreen mode Exit fullscreen mode

Key rule: later-declared layers win over earlier ones. utilities overrides components overrides base overrides reset.

And crucially: all layered styles lose to unlayered styles:

/* This wins over anything in any @layer */
.btn { background: green; }
Enter fullscreen mode Exit fullscreen mode

Unlayered CSS is like an implicit top-priority layer.

Solving Third-Party Override Problems

Push third-party CSS into a low-priority layer:

/* Declare your app's layers first, then the third-party layer */
@layer reset, third-party, base, components, utilities;

/* Import Bootstrap into the low-priority layer */
@layer third-party {
  @import url('bootstrap.css');
}

/* Your components automatically override Bootstrap, no specificity needed */
@layer components {
  .btn { background: #3B82F6; } /* Just works! */
}
Enter fullscreen mode Exit fullscreen mode

This replaces all Bootstrap specificity hacks with a single architectural decision.

Importing into Layers

/* Import a file into a layer */
@import 'reset.css' layer(reset);
@import 'third-party/bootstrap.css' layer(framework);

/* Or assign import layers inline */
@layer framework {
  @import url('bootstrap.css');
}
Enter fullscreen mode Exit fullscreen mode

Nested Layers

Layers can contain sub-layers:

@layer components {
  @layer base, variants, states;

  @layer base {
    .btn { /* Base styles */ }
  }

  @layer variants {
    .btn-primary { /* Primary variant */ }
    .btn-danger { /* Danger variant */ }
  }

  @layer states {
    .btn:hover { /* Hover state */ }
    .btn:disabled { /* Disabled state */ }
  }
}

/* Reference nested layers from outside */
@layer components.states {
  .btn:focus { outline: 2px solid blue; }
}
Enter fullscreen mode Exit fullscreen mode

Anonymous Layers

Layers without names — useful for one-shot CSS blocks that should have low priority:

@layer {
  /* Anonymous layer — can't be referenced again */
  .third-party-widget { color: blue; }
}
Enter fullscreen mode Exit fullscreen mode

Anonymous layers are ordered in document order relative to named layers.

Practical Architecture: Design System Layers

/* Design system layer hierarchy */
@layer
  reset,       /* 1. Normalize browser defaults */
  tokens,      /* 2. CSS custom properties / design tokens */
  base,        /* 3. Element defaults (h1, p, a, etc.) */
  layout,      /* 4. Page structure (grid, containers) */
  components,  /* 5. UI components (btn, card, modal) */
  utilities;   /* 6. Single-purpose utility classes */

/* App-specific overrides outside layers (highest priority) */
Enter fullscreen mode Exit fullscreen mode

Layers and Specificity

Within a layer, specificity still matters:

@layer base {
  a { color: blue; }           /* Less specific */
  nav a { color: inherit; }   /* More specific — wins WITHIN this layer */
}
Enter fullscreen mode Exit fullscreen mode

But cross-layer: later layer always wins regardless of specificity:

@layer first {
  body main section .container a.link { color: blue; } /* Very specific! */
}

@layer second {
  a { color: red; } /* Wins — later layer always beats earlier layer */
}
Enter fullscreen mode Exit fullscreen mode

This is the superpower of cascade layers.

Firefox DevTools: Viewing Layers

Firefox DevTools shows cascade layers in the Inspector:

  1. Open Inspector → click an element
  2. In Styles panel: layers are shown with their layer name (e.g., components)
  3. The layer order affects which styles are overriding which

CSS Cascade Layers are a foundational improvement for CSS architecture — especially in large codebases with multiple teams or third-party integrations.

Top comments (0)