A stylesheet can begin as a handful of sensible rules and gradually become a negotiation between components, utilities, resets, third-party widgets, and old selectors nobody wants to touch. CSS Cascade Layers give that growing system an explicit order. Instead of trying to win every conflict with more specificity, we can decide which groups of styles are allowed to take precedence.
The useful idea is simple: organise rules into named layers, declare the order once, and let the cascade resolve conflicts between those layers before it compares selector specificity. That makes layers an architectural tool, not merely another syntax feature.
The problem layers solve
Specificity is often treated as a ladder: an ID selector beats a class selector, which beats an element selector. But a real project has another question before specificity matters: which part of the stylesheet should be trusted to win? A component rule should usually beat a reset. A deliberate utility should be able to override a component without requiring an increasingly elaborate selector. A third-party widget should not unexpectedly outrank the site’s own design system just because its selector is more specific.
Without an agreed structure, developers respond by adding selectors, moving blocks of CSS, or appending !important. Those fixes may solve one screen while making the next change harder. The stylesheet becomes a record of historical accidents rather than a description of the design system.
Declare the order before writing the layers
A layer order can be declared near the beginning of the main stylesheet:
@layer reset, tokens, base, components, utilities, overrides;
That statement establishes the order for normal declarations: later layers outrank earlier layers. A reset is deliberately weak, base styles establish ordinary defaults, components define their own presentation, and utilities provide intentional small overrides. The final layer can be reserved for exceptional integration fixes rather than becoming a second unstructured stylesheet.
Rules can then be placed inside those named groups:
@layer base {
body {
margin: 0;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
}
@layer components {
.card {
border: 1px solid var(--border);
border-radius: 1rem;
padding: 1.25rem;
}
}
@layer utilities {
.visually-muted {
color: var(--muted);
}
}
The important benefit is that a utility class can win over a component declaration without having to use a selector such as main .panel.card.is-active. The layer order expresses intent more clearly than selector gymnastics.
Specificity still matters, but inside a layer
Layers do not remove specificity. They change the order in which the browser evaluates competing declarations. For normal declarations, the browser first considers origin and importance, then layer order, and then specificity within the relevant layer. Source order still resolves ties. That means a highly specific selector in an early layer can lose to a low-specificity selector in a later layer.
There is an important reversal for !important: the order of important declarations across layers is reversed. Important declarations in an earlier layer outrank important declarations in a later layer. This is intentional, because layers can be used to establish constraints that ordinary component rules should not casually override. It is another reason to keep important declarations rare and document why they exist.
Use layers to contain third-party CSS
One particularly practical use is to control the influence of vendor styles. If a project imports a library stylesheet, it can be placed in a deliberately early layer:
@import url("vendor.css") layer(vendor);
@layer reset, vendor, base, components, utilities;
That gives the site’s own later layers a predictable advantage for normal declarations. It is not a substitute for checking the library’s assumptions, and it does not magically solve every conflict involving inline styles or important declarations. It does, however, create a clear boundary between external defaults and local design decisions.
Avoid turning the layer list into a second mess
Layers work best when their names describe stable responsibilities. A small project might need only reset, base, components, and utilities. A larger system might separate vendor, themes, layouts, and accessibility. There is no prize for inventing twenty layers on the first day.
Do not create a new layer every time a conflict appears. First ask whether the rule belongs in an existing layer, whether the component API is too broad, or whether the selector is targeting the wrong element. A layer should represent a meaningful priority boundary that other contributors can understand.
Also remember that normal unlayered author declarations outrank normal declarations inside named layers. If a project adopts layers, leaving a large amount of new CSS outside them can produce confusing results. Establish a convention and enforce it in review or linting rather than relying on memory.
A migration plan that does not require a rewrite
- Inventory the existing stylesheet and identify resets, global defaults, components, utilities, and vendor code.
- Declare a small layer order at the entry point.
- Move one coherent group at a time; do not mix a layer migration with a visual redesign.
- Test representative pages, including states such as hover, focus, disabled, error, and narrow layouts.
- Check important declarations and unlayered rules explicitly because they can behave differently from ordinary layered rules.
- Document the purpose of each layer so future contributors do not invent competing conventions.
For a site with a long history, a gradual migration is safer than wrapping the entire stylesheet in one enormous layer and hoping the result is unchanged. Start with a part of the system whose dependencies are understood, compare the rendered output, and expand when the rules are stable.
Where to read more
The MDN reference for @layer provides syntax and browser examples. The CSS Cascading and Inheritance Level 5 specification defines the ordering rules, including the reversal for important declarations. These are the right references when a real cascade conflict needs a precise explanation.
CSS Cascade Layers are most valuable when a project has enough styles to need an explicit policy but not enough time to maintain a complicated specificity contest. They make priority visible, reduce accidental coupling, and give a team a shared way to reason about change. The goal is not a clever stylesheet. It is a stylesheet whose next edit is less surprising than the last one.

What do you want to say about this?