Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner Baseline 2022+ ~22 min· topic 5 of 5

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.

main.csswhole filecss
@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.

main.csswhole filecss
@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.

Watch layers beat specificitydiagram
Rendering diagram…

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.

main.csswhole filecss
@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.

main.csswhole filecss
@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.

main.csswhole filecss
@layer base { a { color: navy; } }
@layer components {
  .tag { color: tomato; }
  .tag.plain { color: revert-layer; }   /* back to navy from base */
}

Try it yourself

  1. 1

    Swap the layer order

    In the first example, change the first line to @layer components, base;. Predict the button colour first. (Gray: now base is later, so its ID rule wins.)

  2. 2

    Move a rule out of its layer

    In the first example, cut .lead { color: crimson; ... } out of the components layer and paste it at the very end, unlayered. Predict, then run: now both rules are unlayered, and .lead (0, 1, 0) beats p (0, 0, 1), so the paragraph turns crimson.

  3. 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

A later layer beats an ID; unlayered beats every layer New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Button background: seagreen
Button padding still from base: 10px
Paragraph colour: navy
Paragraph size from components: 20px
Important declarations reverse the layer order; revert-layer New tab

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Paragraph 1: crimson
Paragraph 2: tomato
Paragraph 3: navy
Nested layers, anonymous layers and importscss
/* 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.

terminal
$ the page shows
── what you'll see ──
Every button is gray, even `.btn-primary`, which the components layer makes blue. DevTools shows the layer rule crossed out.

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.

terminal
$ the page shows
── what you'll see ──
Base styles beat component styles: buttons keep the base look.

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 !important in 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 @layer names 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. 1

    A cascade layer is a named group of rules. You create one with an @layer block: @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. 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. 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. 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. 5

    For !important declarations, 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 as framework.base), and an entire file can go into a layer: @import url("bootstrap.css") layer(framework);.

  6. 6

    The revert-layer keyword rolls a property back to the value it would have from the previous layers, ignoring the current one. In unlayered styles it acts like revert. Cascade layers have worked in every major browser since early 2022 (Chrome 99, Firefox 97, Safari 15.4).

Explain it without notes

01

What is a cascade layer, and at which step of the cascade are layers compared?

02

How is the order of layers decided? What happens if the layer statement comes after some layer blocks?

03

How do unlayered styles rank against layered styles, for normal and for important declarations?

04

How would you use layers to make sure your code always beats a third-party CSS framework?

Practice

01

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?

02

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 !important rules become even harder to beat because of the important flip.

Done when you can

  • I can create layers with @layer blocks 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.