This is a CSS feature that allows you to define explicit layers of specificity and fully control the priority of styles in a project, without the use of specificity crutches or !important.
This is how the css style priority scale looks like:
- browser styles + !important;
- user styles + !important;
- author styles + !important;
- regular author’s styles;
- custom user styles;
- normal browser styles.
If a particular style is not defined at a higher level, then the browser moves down the levels until it finds the desired declaration.
If it is not found in the first five levels, then the browser’s default styles will be used.
Whether you write styles with or without preprocessors, using BEM or CSS-in-JS, you are always working inside an “author’s” style source.
When sorting styles (determining priority), several parameters are taken into account (including the source):
- Source and Importance (!important modifier)
- Area of visibility. For example, the shadow DOM creates its own styling context, so the styles within it are independent of the styles of the page itself.
- Inline styles bound to a specific element using the style attribute.
- selector specificity.
- The order in which styles appear in the code works according to the “last one wins” rule.
Within the “author” style source, we can control the last two parameters: the specificity of the rules and the order in which the styles appear. And it’s not easy at all.
Let’s consider a small example. We have a paragraph with the .royal class:
<p class="royal">Lorem, ipsum dolor.</p>
p { color: green; } .royal { color: royalblue; } :first-child { color: red; }
The tag selector has the lowest specificity, so don’t count it.
However, the specificity of a class selector (.royal) and a pseudo-class (:first-child) is the same.
In this case, the browser looks at the order in which the rules appear in the code, and :first-child wins because it comes after .royal. As a result, the text of the paragraph will be red.
If you want to apply the styles of the royal class, you can do the following:
- add the !important modifier to the color definition;
- move the .royal rule after :first-child;
- increase the specificity of a selector by making it, for example, p.royal.
But each of these decisions has consequences:
- !important can make it harder to change the styles of an element later on;
- moving rules is not a panacea, especially if you have many such conflicts;
- increasing specificity can make it harder to reuse styles, for example the .royal class will only work for paragraphs but not for other tags.
The new @-rule @layer is designed to create cascading layers.
The easiest way to create a layer is a direct declaration:
@layer base { body { ... } }
The layer() function can also be used inside an @import rule.
For example, this is very useful when working with third-party libraries, when you immediately place all the styles of the library on a separate layer.
@import url(bootstrap.css) layer(bootstrap);
Also, the @layer rule can be used to specify the order of layers. You just need to list the layer names in the correct order.
@layer bootstrap, base, application;
You can manage the relative position of the layers in one place, without having to move large pieces of code. The order of layers matters a lot, as it affects the priority of styles. The same rule applies here – the last one wins.
You can add styles to previously created layers by referring to them by name:
@layer base { a { } } @layer base { p { } }
Nested layers
Layers can be nested within each other. You can refer to a nested layer through the parent layer using the .(dot), as in JavaScript.
@layer framework { @layer reset, base, components; } @layer framework.reset { ... }
The order of nested layers can also be predefined by listing multiple layers in a single @layer statement:
@layer framework.reset, framework.base, framework.components;
Anonymous layers
In fact, it is not necessary to give a name to the layer, it is quite possible to use anonymous layers.
@layer { /* rules */ } @import url(framework.css) layer;
Anonymous layers can be used as “private” layers if you intentionally don’t want anyone else to be able to manipulate them.
Order within layers
Inside the layers, all the previously considered rules (specificity and order of appearance of selectors) continue to operate. However, at the level of the layers themselves, they cease to matter.
The “later” layer overrides the “earlier” one, regardless of the specificity of the selectors on it.
The specificity of the layer is more important than the specificity of the selector in it.
@layer base { article h2 { color: purple; } } @layer theme { h2 { color: blue; } }
In this example, a simple selector on the h2 tag will take precedence over a more complex selector on the article h2 child element. Therefore, all second-level headings on the page will be blue.
@layer utilities { .blue { color: blue; } } /* win */ p { color: red; }
Styles outside layers take precedence even if they appear in the code before layers, so the order of appearance does not matter here.
@layer typography { p { color: green; } @layer content; } @layer typography.content { p { color: blue; } }
Here everything works exactly the same. Regular styles (“out of layer”) take precedence over layers that are on the same level. The paragraph color will remain green.
As with normal cascading style sorting without layers, the !important modifier increases the precedence of a property declared within a layer. “Important” styles always take precedence over “unimportant” styles on the same or another layer.
// win @layer theme { .lead { color: green !important; } } @layer utilities { .lead { color: red !important; } } .lead { color: orange !important; }
Using Cascading Layers with Preprocessors
CSS preprocessors (Sass, Less) allow you to combine styles from multiple style sheets.
For example, in SASS, you can include other files using the @use directive:
@use "reset"; @use "theme";
Once cascading layers are well supported, you can wrap each cascading connection in a separate layer to create more detailed style sheets. This can be done manually for each file, or you can use upload mixins
@use 'sass:meta'; @layer theme { @include meta.load-css('theme'); }
@use "sass:meta"; @use "sass:list"; $layers: "reset", "theme" !default; // list of layers @layer #{$layers}; // output each layer with appropriate styles @each $layer in $layers { @layer #{$layer} { @include meta.load-css($layer); } }
Browsers support – https://caniuse.com/css-cascade-layers