CSS cascade layers are named groups of style rules, created with the @layer at-rule, whose declared order decides conflicts before selector specificity does. They are specified in CSS Cascading and Inheritance Level 5, a W3C Candidate Recommendation Snapshot dated 13 January 2022. Browsers shipped them in 2022, and web-features lists Chrome 99, Firefox 97 and Safari 15.4 as the first versions.
A layer is created in one of three ways:
@layer base { ... } puts the rules inside into the layer named base.@layer reset, base, components; creates layers with no rules, in the order listed.@import url(theme.css) layer(theme); puts a whole file into a layer. The spec's keyword form layer or layer() makes an anonymous one.Layers are ranked by the order in which each name first appears, anywhere in the stylesheet. A later @layer line that repeats a name does not reorder it. Rules that sit in no layer are treated as one implicit final layer, so they outrank every named layer for normal declarations. For !important the order flips, and the earliest layer wins.
Names can be nested with dots, as in @layer framework.theme, or by putting one block inside another. A parent layer's own rules form an implicit sublayer after its named sublayers, which means they win over them. A block with no name creates an anonymous layer that cannot be added to later.
@layer reset, base, components;
@layer components { .btn { color: green; } }
@layer reset { #main .btn { color: red; } }
@layer base { button.btn.big { color: blue; } }
Markup: <div id="main"><button class="btn big">. Computed color in headless Chromium 141:
#a rgb(0, 128, 0)
The ID rule in reset has far higher specificity than .btn, but components was listed last, so green wins.
Whichever mentions a name first decides it. In a test, a sheet with a components block first, then a base block, then the statement @layer components, base; ended with base on top, and the button color was rgb(0, 0, 255). The late statement did not move components. To control order, put the statement at the top of the first stylesheet, before any @import.
Unlayered rules sit in an implicit last layer, so they win normal declarations against any named layer regardless of specificity. In a test, .btn { color: green; } outside any layer beat #main .btn { color: red; } inside a layer, and the result was rgb(0, 128, 0). This lets your own CSS beat a layered framework without raising specificity.
@layer framework { @layer theme {...} .t {...} }, the direct rule beats the theme sublayer. In a test, both .t and .u colors resolved to rgb(0, 0, 255), the direct rule, even though the theme rule was written first.@layer block cannot be interleaved with @import, and an @layer statement between imports makes later imports be ignored.@layer { } without a name creates a new layer. Name the layer if you need to add rules later.