CSS Cascade Layers: Control Priority with @layer

- Cascade Layers Let You Set Priority Before Selectors Compete
- What Problem Cascade Layers Solve
- The Priority Rules, Exactly
- Declare the Entire Order at the Top
- A Practical Layer Stack
- Import Third-Party CSS into a Layer
- Anonymous and Nested Layers
- The !important Order Is Reversed
- Cascade Layers with Sass and SCSS
- Migrating an Existing Stylesheet
- Common Mistakes
- The Short Version to Remember
Cascade Layers Let You Set Priority Before Selectors Compete
CSS cascade layers use @layer to group styles into an explicit priority order. For normal author styles, a rule in a later layer beats a conflicting rule in an earlier layer even when the earlier selector has higher specificity. Declare the order once, then write modest selectors inside each layer instead of escalating with IDs, deeper nesting, or !important.
Here is the core pattern:
@layer reset, vendor, base, components, utilities, overrides;
@layer base {
a {
color: rebeccapurple;
}
}
@layer utilities {
.text-blue {
color: royalblue;
}
}
An anchor with class="text-blue" is royal blue. The class is more specific than the type selector, but that is not why it wins: utilities was declared after base. The browser compares origin and importance, then layer priority, and only then specificity among rules that remain in the same layer.
What Problem Cascade Layers Solve
Without layers, all normal author rules share one broad arena. A component selector may need to override a framework selector, which leads to a more specific selector. A utility then needs to override the component, so it grows another class. Soon a one-line color change requires a selector with three ancestors and an !important flag.
Layers separate those concerns before selector weight matters. You can assign reset styles low priority, third-party CSS above them, ordinary base styles next, components above base, and intentional utilities or overrides last. The selector inside a later layer can stay simple.
This does not replace understanding the cascade. It adds a priority step to it. If two declarations are in the same origin, importance bucket, and layer, specificity and source order still decide the winner. Read our CSS specificity guide if that part of the decision is still fuzzy.
The Priority Rules, Exactly
For normal author declarations:
- The first layer created has the lowest layer priority.
- Each subsequently created layer has higher priority.
- Normal declarations outside every layer beat normal declarations inside all named and anonymous layers.
- Within one layer, normal specificity and source-order rules apply.
MDN's @layer reference documents those rules and the three declaration forms: a statement that establishes names, a named block containing rules, and an anonymous block.
The unlayered rule surprises people, so see it directly:
@layer components {
#checkout .button {
background: seagreen;
}
}
.button {
background: tomato;
}
The button becomes tomato. The unlayered .button beats the layered #checkout .button for a normal author declaration, despite dramatically lower specificity.
That behavior is useful for intentional last-resort overrides, but it can also puncture your architecture. Once a project adopts layers, either keep ordinary application CSS inside them or reserve unlayered CSS for a clearly documented purpose.
Declare the Entire Order at the Top
The safest setup is a single statement near the top of the entry stylesheet:
@layer reset, vendor, base, components, utilities, overrides;
This creates all six layers and fixes their order. Later, files can reopen any named layer:
@layer components {
.card {
border: 1px solid #d8dee9;
}
}
@layer base {
body {
color: #1f2937;
}
}
Although the base block appears later in this example, components still has higher layer priority because the opening statement created base first and components later. Reusing a name appends rules to that existing layer; it does not move the layer.
This is the architectural payoff: import order can change without silently redefining the intended precedence, provided the layer order statement remains first.
A Practical Layer Stack
| Layer | Put this inside | Keep this out |
|---|---|---|
reset |
Box sizing, margin normalization, sensible element defaults | Brand styling and component states |
vendor |
Framework or third-party stylesheet rules | Local fixes masquerading as vendor code |
base |
Typography, links, document-level defaults | One-off page exceptions |
components |
Buttons, cards, navigation, form controls | Atomic utilities |
utilities |
Single-purpose helpers | Complex components with hidden side effects |
overrides |
Temporary, explicit exceptions | The entire feature stylesheet |
Names are conventions, not reserved words. A small project may need only base, components, and utilities. A design system may prefer tokens, reset, elements, objects, components, and utilities. Choose names that describe responsibility and write the order down once.
Do not create a layer for every file. Layers express precedence groups, not folder structure. Twenty micro-layers make priority harder to understand than the specificity problem they were meant to solve.
Import Third-Party CSS into a Layer
The layer() function on @import assigns an imported stylesheet to a layer:
@layer reset, vendor, base, components, utilities;
@import url("vendor.css") layer(vendor);
Now normal rules in base, components, or utilities can override conflicting vendor rules without copying the vendor's selector weight. The CSS Cascade Level 5 specification lists @import with layer() as one way to assign imported content to a layer.
Keep @import placement legal: it belongs before ordinary style rules. A layer-order statement may precede it, which lets you establish the stack before assigning the import.
If your build tool bundles files, inspect the emitted CSS. You want one coherent layer order followed by rules assigned to those names. A build step that strips unknown at-rules or unwraps blocks incorrectly destroys the architecture. Test the compiled artifact, not just the source tree.
Anonymous and Nested Layers
An anonymous layer has no reusable name:
@layer {
.legacy-widget {
font: inherit;
}
}
It receives a position when created, but you cannot reopen it later by name. That makes anonymous layers reasonable for isolated one-off content and poor for an application structure spread across files.
Named layers can also contain nested layers:
@layer components {
@layer defaults, variants;
@layer defaults {
.button {
background: slategray;
}
}
@layer variants {
.button--primary {
background: royalblue;
}
}
}
Here, components.variants has higher normal priority than components.defaults, but the whole components group still occupies its declared position relative to base and utilities.
Nested layers are useful inside a distributed design system. For an ordinary site, keep the hierarchy shallow. If developers need a map beside the monitor to change a button, the CSS has won a battle nobody asked it to fight.
The !important Order Is Reversed
This is the rule most likely to cause a confident mistake. For important declarations inside layers, the layer order reverses: an important declaration in an earlier layer beats an important declaration in a later layer. Layered important declarations also outrank unlayered important declarations in the same author origin.
@layer base, utilities;
@layer base {
.notice {
color: darkred !important;
}
}
@layer utilities {
.text-blue {
color: royalblue !important;
}
}
An element with both classes becomes dark red, because base was created before utilities and important layer priority runs in the opposite direction.
The reversal protects low-level contracts. A reset or foundational layer can mark a truly critical rule important without a later convenience layer casually defeating it. MDN's cascade-layer tutorial lays out the normal order and its inverted important order.
Do not use this as a reason to add !important everywhere. Use layers to reduce importance battles. When an important declaration is genuinely required, document why it belongs in that layer.
Cascade Layers with Sass and SCSS
@layer is CSS syntax, so it can wrap ordinary SCSS nesting:
@layer components {
.button {
padding: 0.75rem 1rem;
&:hover {
background: royalblue;
}
}
}
Keep the master order in one entry file and place partial contents inside the intended layer. Do not let each partial invent its own global order statement. Our complete Sass guide covers the module system for loading partials, and our comparison of native CSS and Sass nesting explains how the selector syntax differs from layer priority.
Be especially careful when a Sass module emits CSS as a side effect. The module may load once, but wherever its emitted rules land must still match the layer architecture you intend. Compile a representative entry point and inspect the final order.
Migrating an Existing Stylesheet
Do not wrap the entire codebase in six layers all at once. Migrate in visible, testable steps:
- Inventory sources. List resets, vendor CSS, global rules, components, utilities, and emergency overrides.
- Declare a small order. Start with three or four broad layers.
- Layer third-party CSS first. It usually delivers the clearest immediate benefit.
- Move one local concern at a time. Run visual regression tests after each move.
- Find unlayered rules. Decide whether each is intentional or simply missed.
- Audit
!important. Remember the reversed order before relocating any important declaration. - Remove obsolete specificity hacks. Simplify selectors only after the layer behavior is verified.
Use browser developer tools to inspect crossed-out declarations and confirm which layer wins. A rule losing with higher specificity is not necessarily a bug; it may be the exact architecture you declared.
Common Mistakes
Declaring layer names in accidental order
The first creation fixes the order. Put a complete statement at the top instead of relying on whichever file happens to load first.
Leaving most app styles unlayered
Unlayered normal rules outrank every layered normal rule. A single forgotten stylesheet can appear to “break” the stack while behaving exactly according to the cascade.
Expecting layers to beat inline styles or origins universally
Layers operate within an origin and importance context. They are not a top-level override switch for every style source. Keep origin, importance, layer, specificity, and source order conceptually separate.
Moving !important rules without retesting
Normal layer order and important layer order run in opposite directions. A migration that preserves normal behavior can invert important behavior.
Using layers as permission for chaotic selectors
Layers control precedence across groups. Inside one layer, a deeply specific selector is still deeply specific. Keep selectors modest and component boundaries clear.
The Short Version to Remember
Declare layers once from low to high priority, place related CSS inside them, and keep routine application styles out of the unlayered space. Later normal layers beat earlier normal layers before specificity is compared. Important declarations reverse that layer order.
With that model in place, @layer turns the cascade from an accidental result of file order into an explicit part of the stylesheet's architecture.