Cascade Layers as Architecture: Priority Without a Specificity War
Cascade layers make the layer itself a design decision, so specificity becomes a local concern inside a layer instead of a global arms race across your whole stylesheet.
Most teams try to fix a specificity problem with more specificity: a longer selector, another class, eventually an !important.[11] Cascade layers are the evidence that this is the wrong lever, because a single line at the top of a stylesheet can outrank a selector you could never beat by hand.[1][10]
Specificity Is a Tie-Breaker, Not a Control Surface
The cascade is a five-step algorithm: relevance, then origin and importance, then specificity, then scoping proximity, then order of appearance.[2] Specificity is the third step, and it is compared only between selectors that come from the same origin and the same layer.[3] That one sentence is the whole design argument. Before a browser counts a single class or ID, it has already decided which layer wins, and that decision was made by you, in one line, once.[2][3]
So the question to ask is not “how do I make this selector stronger” but “which layer does this rule belong to.”
Unlayered Styles Are Not Neutral
It is tempting to leave your own CSS unlayered and wrap only the vendor styles. But unlayered declarations are treated as belonging to an anonymous final layer that sorts after every explicit layer.[2] Styles outside a layer therefore always override styles inside named and anonymous layers, whatever the selectors say.[1] Skipping layers is not a neutral choice; it is opting into the highest-precedence normal position in the author origin.
That same fact is what makes the reset pattern work: import a reset into a layer and every layer you declare afterwards outranks it.[1][6]
The Layer Order Is Set by First Mention
Layers are sorted by the order in which each name first appears, not by the order of the blocks that carry the rules.[7] Re-declaring a layer name appends styles to it and leaves its position untouched.[1] A single statement at-rule is therefore the architecture:
@layer reset, base, components, utilities;
/* order is now fixed; the blocks below only fill it in */
@layer base {
a { color: var(--link); }
}
@layer utilities {
.stack > * + * { margin-block-start: 1rem; }
}The statement form declares names with no rules attached; the block form declares and fills a layer in one step; the anonymous form takes no name at all, cannot be referenced later, and treats each occurrence as a distinct layer.[1][7] Declaring the order up front also lets you interleave layers with imported ones, which the block syntax cannot do.[7]
Important Declarations Invert the Order
Add !important and the layer order reverses: important declarations in the earliest layer take precedence over important declarations in later ones, and every important layered declaration beats an important unlayered one.[2][5] The spec frames this as the flag maintaining the same override purpose in both settings.[7]
The architectural consequence is that your first declared layer is also your safety net. If you keep a guards layer at the very top for accessibility and forced-colors corrections, its !important rules are the hardest thing in the file to override, which is exactly what you want, and exactly why that layer should stay small.[1][5] Specificity does not disappear, either: when two important declarations sit in the same origin and layer, the more specific one wins.[5] Layers make specificity local rather than abolishing it.
Importing Third-Party CSS into a Layer
The highest-value move is on @import. Importing a stylesheet with a layer assignment places its contents in that layer, and declarations from the imported file interact with the cascade as if they had been written literally at the point of the import.[6] Import a framework into your first layer and your own later layers outrank it with no !important and no specificity escalation.[2][6]
Placement is not optional: an @import rule must come before all other at-rules and style rules except @charset and @layer statements, or it is ignored.[6] That is one more reason the layer statement goes on line one.
Nesting, and Why Libraries Use Anonymous Layers
Layers nest, and the dotted form is shorthand for that nesting, concatenating the segments in order.[7] A nested layer cannot escape its parent to reference layers outside itself.[7]
That makes nesting the right tool for vendoring. A library can wrap each internal file in an anonymous layer and then re-import the whole set under one public name, so consumers can address the public layer but not the internal ones.[7] Anonymous layers cannot be referenced from the outside at all, and two unnamed layers remain two different layers even when their child names match.[7]
revert-layer: Backing Out of One Layer
The revert-layer keyword rolls a property back to the value it holds in a previous cascade layer that matched the element, as if no rule had been specified in the current layer.[4] It differs from revert, which rolls back past the author origin entirely to the user or user-agent origin.[4] If nothing in an earlier layer matches, it keeps rolling back to a previous origin.[4]
The practical use is a component that has to shed a theme value without knowing what that value was, and without reaching for !important to do it.
Two Ordering Hazards
First, layers declared inside a conditional group rule do not contribute to the layer order unless the condition is true.[7] A layer first mentioned inside a @media block can therefore land in a different position depending on the viewport or the user’s color-scheme preference.[7] The fix is the same line as before: establish the order explicitly and avoid introducing new names inside conditional rules.[7]
Second, layer order is scoped to an origin and its context, so identically named layers in the light DOM and inside a shadow root are independent of each other.[7] A web component’s internal layer scheme cannot be disturbed by the host page, and cannot be relied on to coordinate with it either.
What It Looks Like in Production
Tailwind CSS v4 is built on exactly this shape. Its entry stylesheet opens with the ordering statement and then imports each part into its layer:[12]
@layer theme, base, components, utilities;
@import './theme.css' layer(theme);
@import './preflight.css' layer(base);
@import './utilities.css' layer(utilities);That is the framework’s entire cascade architecture in four lines, and it is what lets your own unlayered CSS beat every generated utility class without a single !important.
Support and a Migration Order
@layer is Baseline widely available, shipping in Chrome 99, Firefox 97, and Safari 15.4.[8] revert-layer ships on the same versions.[4][9]
When migrating, order the work by risk: import vendor CSS into the first layer, wrap the reset next, then move component styles in from lowest to highest precedence, keeping the guards layer first so its !important rules stay strongest.[5][6] The last thing to layer is the CSS you are still actively fighting, because the point of the exercise is that you should have to fight it less.[1]
Sources
[1] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@layer — MDN — @layer CSS at-rule
[2] https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Introduction — MDN — Introduction to the CSS cascade
[3] https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascade/Specificity — MDN — Specificity
[4] https://developer.mozilla.org/en-US/docs/Web/CSS/revert-layer — MDN — revert-layer CSS keyword
[5] https://developer.mozilla.org/en-US/docs/Web/CSS/important — MDN — !important CSS keyword
[6] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@import — MDN — @import CSS at-rule
[7] https://drafts.csswg.org/css-cascade-5 — CSS Cascading and Inheritance Level 5 (W3C editor's draft)
[8] https://raw.githubusercontent.com/mdn/browser-compat-data/main/css/at-rules/layer.json — mdn/browser-compat-data — css.at-rules.layer
[9] https://raw.githubusercontent.com/mdn/browser-compat-data/main/css/types/global_keywords.json — mdn/browser-compat-data — revert-layer global keyword
[10] https://raw.githubusercontent.com/web-platform-dx/web-features/main/features/cascade-layers.yml — web-features — cascade-layers.yml
[11] https://www.bram.us/2021/09/15/the-future-of-css-cascade-layers-css-at-layer — Bramus — The Future of CSS: Cascade Layers (@layer)
[12] https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/packages/tailwindcss/index.css — tailwindcss — packages/tailwindcss/index.css