Topic 2.5
Cascade Layers
In one line
Cascade layers (@layer) group rules into named layers whose order you declare up front; a later layer beats an earlier one before specificity is even compared, so resets, frameworks and your own code stop fighting.
Think of it like this
A stack of transparent sheets on an overhead projector. The bottom sheet has the basic drawing, the next adds colour, the top one adds labels. Whatever is on a higher sheet covers what is below, no matter how thick the pen was on the lower sheet. Cascade layers are those sheets: the order of the sheets matters more than how "strong" each mark is.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Cascade layer
- A named group of CSS rules. Later layers beat earlier layers, whatever the selectors.
- @layer block
@layer name { ...rules... }: puts rules into a layer.- Layer statement
@layer a, b, c;at the top of the CSS: declares the layer order without adding rules.- Unlayered styles
- Rules not inside any layer. They beat all layered rules (for normal declarations).
- Anonymous layer
- A layer created with
@layer { ... }and no name. It can't be reopened later. - Nested layer
- A layer inside another layer, named with a dot:
framework.base. - revert-layer
- A keyword that rolls a property back to what earlier layers set.
- Reset
- A small stylesheet that removes or evens out browser default styles before your own styles.
Step by step
01Declare the order first
Start your main stylesheet with one layer statement. It names every layer, from lowest to highest priority. This one line is the map of your CSS.
@layer reset, base, components, utilities;02Put rules in layers
Write rules inside @layer blocks. The blocks can come in any order and in any file: the statement already fixed their ranking.
@layer reset, base, components, utilities;
@layer utilities {
.mt-0 { margin-top: 0; }
}
@layer reset {
* { margin: 0; box-sizing: border-box; }
}
@layer base {
body { font-family: system-ui, sans-serif; line-height: 1.5; }
h1 { margin-top: 2rem; }
}
@layer components {
.card h1 { margin-top: 1rem; }
}03Watch layers beat specificity
<h1 class="mt-0"> inside a card: .card h1 (0, 1, 1) in components and .mt-0 (0, 1, 0) in utilities both set margin-top. Without layers the more specific .card h1 would win. With layers, utilities is later, so .mt-0 wins and specificity is never compared.
04Layer a framework you don't control
Import a third-party stylesheet straight into a layer. Your own unlayered or later-layered CSS now beats it, however specific its selectors are. @import rules must come before other rules, except @charset and layer statements.
@layer framework, components;
@import url("bootstrap.min.css") layer(framework);
@layer components {
.btn { border-radius: 999px; } /* beats Bootstrap's .btn, whatever its specificity */
}05Know the important flip
Important declarations run in reverse layer order. A reset that says [hidden] { display: none !important; } in the lowest layer beats an important declaration in any later layer, and in unlayered code. Low layers use this to guarantee a few values.
@layer reset {
[hidden] { display: none !important; } /* wins over any later !important */
}06Roll back with revert-layer
Inside a layer, revert-layer says "ignore what this layer set for this property, use what earlier layers gave". It's handy for variants that should fall back to the base style.
@layer base { a { color: navy; } }
@layer components {
.tag { color: tomato; }
.tag.plain { color: revert-layer; } /* back to navy from base */
}Try it yourself
- 1
Swap the layer order
In the first example, change the first line to
@layer components, base;. Predict the button colour first. (Gray: nowbaseis later, so its ID rule wins.) - 2
Move a rule out of its layer
In the first example, cut
.lead { color: crimson; ... }out of thecomponentslayer and paste it at the very end, unlayered. Predict, then run: now both rules are unlayered, and.lead(0, 1, 0) beatsp(0, 0, 1), so the paragraph turns crimson. - 3
Remove the lowest important
In the second example, delete the rule inside
@layer reset. Predict paragraph 1's colour (seagreen: among the remaining importants, the components layer beats unlayered).
Code & diagrams
The cascade runs per property: `padding` had no competitor, so the base layer's value applies. `background` and `color` are decided by layer order before specificity.
Preview
Console
Nothing logged yet.Expected console output
Button background: seagreen
Button padding still from base: 10px
Paragraph colour: navy
Paragraph size from components: 20pxPreview
Console
Nothing logged yet.Expected console output
Paragraph 1: crimson
Paragraph 2: tomato
Paragraph 3: navy/* order: framework (with its own sub-layers), then app */
@layer framework, app;
@import url("framework.css") layer(framework);
@layer framework {
@layer base, components; /* framework.base, framework.components */
}
@layer framework.components { /* dot notation reopens a nested layer */
.btn { padding: 8px 16px; }
}
@layer app {
.btn { padding: 10px 20px; } /* app beats every framework layer */
}
@layer { /* anonymous: placed here, can't be reopened */
.debug { outline: 1px dashed red; }
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Layer your own code, forget the leftovers
Move your component styles into @layer components, but leave an old unlayered rule button { background: gray; } at the bottom of a forgotten file.
Break #2
Declare the order too late
Write @layer components { ... } first and @layer base { ... } after it, then add @layer base, components; at the end of the file.
Myth vs fact
Myth
Rules in later layers win only if their selectors are at least as specific.
Fact
Layer order is compared before specificity. A single class in a later layer beats an ID in an earlier layer.
Myth
Putting your code in a layer makes it stronger.
Fact
For normal declarations, unlayered styles are the strongest. Layering your code makes it weaker than any unlayered rule.
Myth
!important in a later layer beats !important in an earlier one.
Fact
For important declarations the order reverses: the earlier layer wins, and layered importants beat unlayered importants.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Layers replace many old tricks: no more specificity hacks to beat a framework, no more
!importantin your overrides, and less dependence on the order of<link>tags, because the layer statement fixes the order once. - ▸
A layer statement must be the first thing that mentions a layer. With several stylesheets, put
@layer reset, framework, base, components, utilities;in a tiny first<style>in the<head>, so the order holds no matter which file loads first. - ▸
Shadow DOM has its own layers: a component's
@layernames are scoped to its shadow tree, and the outer page's layers can't reorder them. - ▸
Tailwind CSS v4 ships its styles as
@layer theme, base, components, utilities;using native cascade layers, so your unlayered CSS beats its utilities unless you put it in a layer.
Remember this
- 1
A cascade layer is a named group of rules. You create one with an
@layerblock:@layer base { h1 { font-size: 2rem; } }. You set the order of all layers with a layer statement at the top of your CSS:@layer reset, base, components, utilities;. Layers listed later have higher priority. - 2
Layers are step 3 of the cascade (Topic 2.1), before specificity. So
@layer components { .btn { color: white } }beats@layer base { #save { color: gray } }, even though the ID is more specific. Inside one layer, specificity and order work as usual. - 3
Styles written outside any layer (unlayered styles) form an implicit final layer, the strongest one. This is what lets you adopt layers gradually: put a framework or a reset into a layer, and all your existing unlayered CSS automatically beats it.
- 4
A layer's position is fixed the first time its name appears. A layer statement at the top fixes the order for the whole page, whatever order the blocks come in later, and blocks with the same name are merged into one layer. Without a statement, layers are ordered by where their first block appears. A block with no name (
@layer { ... }) creates an anonymous layer that can't be added to later. - 5
For
!importantdeclarations, layer order reverses: an important declaration in an earlier layer beats one in a later layer, and important layered styles beat important unlayered ones. That lets a low layer such as a reset protect a value on purpose. Layers can also be nested (@layer framework { @layer base { } }, referred to asframework.base), and an entire file can go into a layer:@import url("bootstrap.css") layer(framework);. - 6
The
revert-layerkeyword rolls a property back to the value it would have from the previous layers, ignoring the current one. In unlayered styles it acts likerevert. Cascade layers have worked in every major browser since early 2022 (Chrome 99, Firefox 97, Safari 15.4).
Explain it without notes
What is a cascade layer, and at which step of the cascade are layers compared?
How is the order of layers decided? What happens if the layer statement comes after some layer blocks?
How do unlayered styles rank against layered styles, for normal and for important declarations?
How would you use layers to make sure your code always beats a third-party CSS framework?
Practice
Write CSS with three layers, reset, base and components. The reset removes all margins, base gives p a bottom margin of 1em, and a .card p component rule sets its margin to 0.5em. Which margin does a paragraph inside .card get?
A framework's .btn { background: blue; } is unlayered and loaded before your stylesheet. You add @layer app { .btn { background: green; } }. What colour is the button, and how do you fix it?
Trade-offs
- ↔
Layers make priority explicit and order-independent, but add one more concept to the cascade that everyone on the team must understand; a forgotten unlayered rule silently beats all layered code.
- ↔
Putting a third-party stylesheet in a layer is the cleanest way to override it, but its
!importantrules become even harder to beat because of the important flip.
Done when you can
I can create layers with
@layerblocks and order them with a layer statement.I know layers are compared before specificity.
I know unlayered styles beat layered ones, and that important declarations reverse the order.
I can import a stylesheet into a layer.
I can use nested layers and
revert-layer.