Cascade Layers
@layer groups rules into named layers whose order you control, independently of how specific the
selectors inside them are. It is the CSS at-rule described at
MDN Docs, and its main use is keeping a
framework’s rules from outranking your own.
@layer framework, app;
@layer framework {
#title {
color: white;
}
}
@layer app {
text {
color: red;
}
}
#title is far more specific than text, and loses anyway: app is the later layer, and the layer
order is consulted before specificity.
Declaring layers
| Form | What it does |
|---|---|
@layer name { … } | Puts the rules in name, creating it if it is new |
@layer name; | Creates name in this position without adding rules |
@layer a, b, c; | Creates several at once, in that order |
@layer { … } | An anonymous layer, which nothing else can add to |
@layer outer { @layer inner { … } } | A sub-layer of outer |
@layer outer.inner { … } | The same sub-layer, addressed by its full name |
A layer takes its position from where its name is first seen in the document, so reopening it
later adds rules to the original position rather than moving it to the end. That is what makes the
leading @layer a, b, c; statement worth writing: it fixes the whole order up front, whatever
order the blocks then arrive in.
How layers order
- Unlayered rules beat every layer, wherever they appear.
- Among layers, the later layer wins.
- Inside one layer, specificity and source order decide as usual.
- A sub-layer sits inside its parent’s slot, and loses to the parent’s own rules. So
outer.innerloses toouter, and both lose to any layer declared afterouter. !importantreverses 1 and 2: an important declaration in the earliest layer wins, and an important declaration in any layer beats an important unlayered one.
Layers nest inside @media and @supports in either order. A layer declared inside a @media
block still takes its position from the source, so a condition turning on or off never reshuffles
the order of rules that have nothing to do with it.
@layer base {
view {
background-color: black;
}
}
@media (min-width: 600px) {
@layer base {
view {
padding: 20px;
}
}
}
Layers and stylesheets
The layer order belongs to the whole context rather than to one stylesheet. A name means the same
layer in every sheet, and a layer takes its place from the first sheet that mentions it — so a
later sheet reopening a layer adds to it where it already is, instead of making a new one at the
end. That is what makes a leading @layer statement worth putting in a sheet of its own.
/* inserted first: fixes the order for everything that follows */
@layer framework, app;
/* inserted later: lands in the slot above, not after `app` */
@layer framework {
#title {
color: white;
}
}
Removing a stylesheet gives up the layers that only it declared, so the order is always what the
currently attached sheets say it is. Inserting or removing one can therefore move a layer — a sheet
declaring framework.reset slots that in ahead of framework — and the rules already in the
layers that moved are reranked to match.
revert-layer
revert-layer rolls a declaration back to the value it would have had if its own layer had never
been declared. It is the CSS keyword described at
MDN Docs.
@layer base {
text {
color: black;
}
}
@layer theme {
text {
color: white;
}
#title {
color: revert-layer;
}
}
#title is black. The whole of theme goes, not just the declaration that reverted it, so the
color: white in the same layer is passed over as well and base is what remains. It keeps rolling
back as far as it has to: if the value it lands on is a revert-layer too, that layer goes next.
revert-layer in an unlayered declaration has no earlier layer to reach, so it rolls back
further than the layers — past every rule of the same origin, which is what revert does. The same
is true of one in the earliest layer with nothing behind it.
An !important declaration reverts its layer at its own priority, and the layer order for those is
reversed, so revert-layer !important in the earliest layer lands on the important declaration in
the next layer along.
Limitations
A context has room for 30 distinct layer positions. Beyond that, every further layer shares the last position, so they fall back to specificity and source order against each other. Sub-layers count towards the same 30.
@import is not implemented, so @import url(…) layer(name) has nothing to attach to.