Cascade layers is a new API that is currently being implemented into our browser. As of writing this, the support is quite impressive already.
Cascade layers allow encapsulation of our CSS rules that can be ordered by specificity. It offers cascade management without the hassle of specificity hacks and abuse of !important
.
With CSS, one of the things I see people struggle the most with is writing selectors the right way. There are tons of ways to write a selector that will produce the same results, and it can be confusing.
When a selector isn’t working as intended, what should we do?
Should we “artificially” increase its specificity by adding selectors we don’t need?
Should we move it down our CSS file?
Should we reduce the colliding selector specificity?
BEM coupled with SCSS helps a lot to reduce these issues.
The principle is to namespace class names, so we can select anything using only one class selector. Everything has the same specificity.
A promising idea on paper, but it has its caveats on a large codebase spread around large teams.
Cascade layers allow us to encapsulate specificity of selectors to avoid collision in our style.
How Does the Cascade Work?
To use cascade layers, we first need to understand how the cascade worked before its introduction.
The cascade is how the browser resolves conflicts between rules affecting the same element.
We can interact with it using the selector specificity and adding !important
to our declarations.
The Cascade Origin
Before Cascade layers, we already had Cascade layers of some sorts. We called them cascade origins.
There are 3 origins for CSS rules:
- User agent styles are the default styles from your browser, they define spacing, font sizes and set a lot of defaults
- User styles are provided by the user, but most browser will only let you choose a font size and family
- Author styles are the one we – the CSS developers – write for a living, and it has the highest specificity
Different origins get different specificities
These origins encapsulate the specificity, exactly like Cascade layers do.
If you define a rule with a high specificity in the user origin, it can be overwritten with a lower specificity selector in the author style, because the author origin has a higher specificity than the user origin.
The confusing way !important
works
Since user styles have higher specificity than browser style, we’d expect an !important
rule in user style will have higher specificity than an important rule in browser style, right?
Well, not exactly. Because when using !important
, the cascade origin order is reversed.
Meaning the lowest specificity origin has the highest for rules using !important
.
Cascade origins, from least specific to most:
- Browser declarations
- User declarations
- Author declarations
-
!important
Author styles -
!important
User styles -
!important
Browser styles
Cascade origins order is reversed when using !important
Adding Cascade Layers in the Mix
Now that we studied how the cascade worked before the introduction of the Cascade layers API, let’s see what this new API brings to the table.
CSS Cascade layers provides a @layer
rule we can use to define cascade layers.
These layers contain the specificity of selectors, meaning a high specificity selector can easily be overwritten by a lower specificity selector in a higher specificity layer.
The @layer
keyword wrap CSS selectors like media queries would do:
@layer reset {
.link {
color: blue;
}
}
@layer base {
a {
color: red;
}
}
In this example, an a
element with the link
class will appear red, since the base
layer has a higher specificity.
⚠️ Note that non-layered selectors have higher specificity than any layered selector.
The later a layer is defined, the higher its specificity, but we can declare their order up-front.
In this example, the reset layer has the lowest priority beside its rules living later in the code:
@layer reset,base,utilities;
@layer base {
...;
}
@layer utilities {
...;
}
@layer reset {
...;
}
Also, we can encapsulate whole files into a layer using @import
:
@import url("reset.css") layer(reset);
The usage of !important
Note that the usage of !important
in layers works the same way that in the origins, meaning the !important
rules in the lowest specificity layer have the higher specificity.
Conclusion
I find this way of encapsulating CSS very useful. I can imagine using a date picker JS library that needs some CSS. By encapsulating that CSS into a layer and making it a higher priority than the base CSS, we ensure that none of our base styles will override that library CSS.
I strongly recommend you to pass this short quiz on CSS Tricks to test your knowledge on cascade layers.
Sources:
- CSS Cascading and Inheritance Level 5: the W3C draft for the feature
- A Complete Guide to CSS Cascade Layers: A very complete and detailed guide on the matter, by Miriam Suzanne
- CSS Cascade Layers Explained by Pierre H.
- Hello, CSS Cascade Layers by Ahmad Shadeed
I'm Tom Quinonero, I write about design systems and CSS, Follow me on twitter for more tips and resources 🤙
Top comments (2)
I suggest you don't use the word "specificity" here. "Specificity" has a particular meaning in CSS that doesn't fit in this context. Specificity operates within each cascade layer, as it does with within each origin+importance, not across them. "Priority" is more appropriate.
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍