Articles

CSS Cascade Layers Explained: The @layer Rule

CSS cascade layers let you group styles into named layers with explicit priority order, so specificity fights between resets, components, and overrides disappear.

Takina Takina · · 4 min read
A close-up of CSS code on a screen

Cascade layers, defined with the @layer rule, let you group CSS declarations into named layers and control which layer wins when styles conflict — independent of selector specificity or source order. Instead of fighting specificity by stacking classes or reaching for !important, you declare the priority of entire groups of styles up front.

The problem cascade layers solve

Without layers, the browser resolves conflicting declarations using a fixed set of rules: origin (browser styles, then author styles, then !important), then specificity (ID beats class beats element), then source order (later wins ties). This works fine for small stylesheets, but it breaks down once a project combines a CSS reset, a component library, a design system, and page-specific overrides. A single .button--primary class from a component library can end up harder to override than intended, simply because it was written with a more specific selector than the page-level style trying to change it. Teams historically worked around this with strict naming conventions or by reaching for !important, which just moves the specificity war to a different battlefield.

Cascade layers add a new axis to the cascade that sits before specificity is even considered: layer order. A rule in a later-declared layer beats a rule in an earlier layer, no matter how specific the earlier rule’s selector is.

Basic syntax

Declare layer order up front, then write each layer’s rules:

@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; }
}

@layer components {
  .button { padding: 0.5rem 1rem; border-radius: 4px; }
}

@layer utilities {
  .p-0 { padding: 0; }
}

Here, utilities always beats components, which always beats reset — regardless of selector specificity or where the rules physically appear in the file. .p-0 overriding .button’s padding works even though .button isn’t present in that rule, because the entire utilities layer outranks the entire components layer.

Unlayered styles — plain CSS not wrapped in any @layer block — always win over any layered style, acting as an implicit final layer. This matters when introducing layers into an existing codebase: your existing unlayered CSS keeps overriding new layered styles until you either wrap it in a layer too or intentionally leave it as the top-priority escape hatch.

Nesting and importing into layers

Layers can be nested for finer-grained grouping:

@layer base {
  @layer typography, spacing;

  @layer typography {
    body { font-family: system-ui; }
  }
}

You can also assign an entire imported stylesheet to a layer, which is particularly useful for third-party CSS:

@import url("normalize.css") layer(reset);
@import url("vendor-components.css") layer(vendor);

This lets you slot an entire vendor stylesheet into a specific priority position without touching its source, and guarantee your own components or utilities layer beats it regardless of how specific the vendor’s selectors are.

Cascade layers vs specificity wars

Specificity-based overridesCascade layers
Priority mechanismSelector specificity + source orderExplicit layer declaration order
Overriding a library styleRequires matching or exceeding its specificityAny rule in a later layer wins automatically
ReadabilityPriority is implicit, hard to reason aboutPriority is declared once, up front
Common workaround!important, ID selectors, deeply nested classesNot needed — layer order handles it

Cascade layers don’t eliminate specificity — it still resolves conflicts within the same layer. What they eliminate is needing to think about specificity across layers, which is where most real-world override headaches come from.

Where cascade layers fit with modern CSS

Cascade layers pair naturally with CSS nesting for organizing component styles, and with CSS custom properties for theming — you can put your design tokens in a tokens layer that everything else safely reads from without accidental override risk. If your project uses Tailwind CSS, it’s worth knowing that recent Tailwind versions use cascade layers internally to separate base styles, components, and utilities, which is part of why utility classes reliably override component styles without specificity tricks. Understanding @layer also makes it easier to reason about CSS specificity in general, since layers are now the first thing the cascade checks, before specificity ever comes into play.

Browser support and adoption

Cascade layers are part of the CSS Cascade Layers specification and are supported in all major modern browsers. As with any relatively recent CSS feature, check current support against your project’s minimum browser targets before relying on it for anything critical, and have a fallback plan (typically: your existing specificity-based approach still works fine as a base layer) for the tail of older browsers if you need to support them.

The takeaway

Cascade layers give you an explicit priority axis — declared once via @layer name1, name2, ... — that resolves conflicts before specificity or source order are even considered. That makes large stylesheets combining resets, component libraries, and utility classes far easier to reason about, since a later layer always wins over an earlier one regardless of how specific its selectors are. Start by wrapping a reset and a component library in their own layers; the payoff compounds as more of the stylesheet moves into the system.

Takina Takina · · 4 min read

CSS object-fit and object-position, Explained

object-fit controls how an image or video is cropped inside its box, and object-position controls which part of it stays visible. How they work together.

#CSS #Web Development #Frontend
Takina Takina · · 5 min read

CSS inherit, initial, unset & revert Explained

CSS's four global keywords control where a property's value comes from. How inherit, initial, unset, and revert differ, with a comparison table.

#CSS #Web Development #Frontend
Takina Takina · · 4 min read

Dynamic Viewport Units: dvh, svh, and lvh Explained

dvh, svh, and lvh fix the classic mobile vh bug where browser toolbars cut off full-height layouts. Here's what each unit measures and when to use it.

#CSS #Web Development #Frontend