If you have been fighting coding CSS long enough you might have seen this a handful of times, you import some library and want to override its styles, you look for your selectors and write some rules but then you refresh and your changes aren't there. You either didn't use enough specificity or the library styles are getting imported after your changes, or in bigger projects someone else used the library and ended up reimporting its styles down the line. What follows is a blind hunt and debugging for the proper order of your style imports, or an endless string of selectors to beat the library specificity (and god forbid if you instead begin spamming
!important
everywhere).It’s bound to be a better way, a way to control the cascade order at our will and force the CSS gods to do our bidding.
@layer
is one of these modern solutions, it's part of baseline 2022 and even though it’s relatively recent it already features great support and even has a polyfill available for legacy browsers. So how does it work?
We can declare
@layer
in the order of our choosing and then group our rules under them, the order of declaration ends up being its priority and the more recent is always triumphant over the previous ones. The syntax is super easy and looks something like this:
@layer your-base-layer, your-other-layer; @layer your-base-layer { p { color: red; } } @layer your-other-layer { p { color: blue; } }
@layers
are sorted by order of appearance and can be declared before filling them, for example @layer layer-one @layer layer-two @layer layer-three
we can then call them again to add rules or even assign
@layers
directly when importing @import "library.css" layer(your-layer-name);
The best part? layers besides taking precedence over order also override specificity. So it doesn't matter how deep the selector you want to override goes, as long as you are on a top layer your styles will always apply. How fancy is that?
Moreover, rules declared outside layers end up grouped and put into an anonymous layer that gets added to the end thus triumphing over all the other layered rules.
Here is an interactive CodePen, pay attention to how our first declaration is the one triumphant even though the layer is declared after and its selectors have more specificity
Important Note: You can’t change layer order later on, so it’s better to declare them ASAP in your stylesheets to have precise control over them.
Advanced Rocket Science
@layers can also be nested, this allows styles and layers from other teams or libraries to be imported even if they use layers. To access a nested layer you join them with
.
@layer your-layer.your-nested-layer{ p { color: purple; } }
Another interesting feature is binding layers to features and media queries this way you can change the order of precedence only if the query is met
@media (min-width: 800px) { @layer first-layer; } @layer second-layer{ p { color: blue; //this style would win if the media query is met } } @layer first-layer{ p { color: red; //this style would win if the media query isn't met } }
We can also dynamically import files and bind them to layers for some advanced media queries and better project organization
@import url("narrow.css") layer(your-layout-layer) supports(width < 32rem);
Getting stuff done
- Begin by declaring some layers in your first imported file.
- Be sure to move your reset.css import to the first layer so it doesn't mess up your styles.
- Regain control of your styles!
Gotchas
If your project uses Sass you can't directly import SCSS files to layers, you'll need to compile them to CSS or use the following Sass module. (keep in mind your sass variables will end up converted to their declared value)
@use 'sass:meta'; @layer reset { @include meta.load-css('./reset'); }
Better yet why not take the chance to drop Sass altogether, almost no reason to use it anymore when CSS nesting is here and CSS variables are way more powerful.
Links
Layer Polyfill https://www.oddbird.net/2022/06/21/cascade-layers-polyfill/