Introduction to Cascade Layers in CSS

Get familiar with this new API that let us scope selector specificity

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

Different origins get different specificities

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:

  1. Browser declarations
  2. User declarations
  3. Author declarations
  4. !important Author styles
  5. !important User styles
  6. !important Browser styles
Cascade origins order is reversed when using !important

Cascade origins order is reversed when using !important

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.

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.

In the author style, the `!important` is also reversed

In the author style, the !important is also reversed


Think you got everything right?

Let's take a quiz to check if you understood everything.

CSS Cascade Layers Quiz


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'm Tom Quinonero, I write about design systems and CSS, Follow me on twitter for more tips and resources 🤙