Skip to main content

CSS Cascade Layers (@layer)

CSS Cascade Layers, introduced by the @layer at-rule, provide a powerful mechanism for organizing CSS and controlling the cascade more predictably. They solve a long-standing problem in large-scale CSS projects: how to manage specificity conflicts between different sources of styles (e.g., framework, base, components, and utilities).

The Core Concept: Styles within a higher-priority layer will always win over styles in a lower-priority layer, regardless of the selector's specificity within those layers.


1. The Cascade Layer Order

The final result of a style depends on a fixed order of precedence. Cascade Layers slot into the standard cascade but allow you to define a sub-order for your own styles.

The Key Layer Precedence Rule:

  • Earlier layers have lower priority (less important).
  • Later layers have higher priority (more important).

This means a simple selector in a late-defined layer can override a highly specific selector in an early-defined layer.

Order of Author Styles (Lowest to Highest Priority)

  1. Styles within the first defined @layer (e.g., reset).
  2. Styles within later defined @layers (e.g., utilities).
  3. Unlayered Styles (CSS not wrapped in any @layer block).
  4. Author !important declarations.

2. Defining and Ordering Layers

You must define the order of your layers using a single @layer statement, typically placed at the very top of your CSS file.

2.1. Defining the Order

/* 1. Define the layer order (Lowest to Highest Precedence) */
@layer reset, framework, base, components, utilities;

In this setup: reset has the lowest priority, and utilities has the highest priority among the layers.


2.2. The Specificity vs. Layer Rule

This is the most critical concept to understand:

Layer Order Trumps Specificity: The specificity of a selector only matters within its own layer. Once two properties conflict across two different layers, the layer order determines the winner, ignoring specificity.

Layer Wins

Consider two rules:

  1. .box (specificity: 0,1,0) in the base layer.
  2. #box-id (specificity: 1,0,0) in the utilities layer.

If utilities is defined after base, the simple .box utility will override the highly specific #box-id selector, because the utilities layer has higher precedence. This is how layers flip the cascade.

2.3. Creating Layer Blocks

You then wrap your styles in named @layer blocks. You can define layers across multiple files or multiple blocks in the same file; the browser will stitch them together based on the initial ordering statement.

styles.css
/* -- 1. Reset Layer (Lowest Priority) -- */
@layer reset {
/* This universal selector style is easily overridden by any other layer. */
* {
box-sizing: border-box;
}
}

/* -- 2. Components Layer (Mid Priority) -- */
@layer components {
/* Specific component style */
.card-title {
color: var(--theme-color);
}
}

3. Unlayered Styles (The Override)

Styles that are not included in any @layer block are referred to as unlayered styles.

Unlayered styles always have a higher precedence than all layered styles, regardless of their position in the file or the layer order.

Unlayered Priority

Use unlayered styles sparingly, typically only for final, non-negotiable overrides or small third-party snippets where integrating them into a layer is impractical. Relying heavily on unlayered styles defeats the purpose of managing the cascade with layers. The goal should be to make 99% of your styles layered.

styles.css
/* This style is NOT wrapped in @layer. Specificity: 0,1,0 */
.final-override {
/* This will win over ALL layered styles, even if the selector is simpler. */
background-color: green;
}

/* This is a layered style that loses to the unlayered style above. Specificity: 0,1,0 */
@layer utilities {
.final-override {
background-color: red;
}
}

4. Layer Organization and Naming

A common layer strategy is to move from the least specific, broadest styles to the most specific, overriding styles.

Layer NameTypical ContentPriorityRole
resetCSS resets (e.g., normalize.css), global box-sizing.LowestProvides a clean slate.
frameworkThird-party libraries (Tailwind base, Bootstrap).LowImports external defaults.
baseElement selectors (h1, p, a), custom fonts, root variables.Mid-LowDefines project defaults.
componentsReusable, complex class selectors (e.g., .modal, .card).Mid-HighDefines component structure.
utilitiesSingle-purpose, overriding classes (e.g., .u-hidden, .u-text-red).HighestEnsures utility classes always win.

Nested and Imported Layers

Layers can be nested, and styles can be imported directly into a layer, making framework integration seamless.

Layer Nesting

Nesting layers helps organize large component libraries (e.g., separating forms from navigation).

styles.css
/* Initial layer declaration must include the top level: */
@layer framework, components;

/* Importing an external file into the 'framework' layer */
@import url('bootstrap.css') layer(framework);

/* Defining a nested layer within components */
@layer components.forms {
/* Styles for form components */
.input-field { border-color: gray; }
}

Styles in components.forms have the same precedence as components styles.


Interactive CSS Layers Demo

This example demonstrates the precedence: The utility layer is defined later than the component layer, so the utility's style always wins. The unlayered style wins over both.