Cascade Layers

Today, I want to talk to you about cascade layers.

It's a new and exciting way by which we get to write our CSS.

Because with cascade layers, we can slice up our style sheet into several layers, and then we get to control the order of these layers.

It's really exciting stuff.

But before we dive into cascade layers itself, let's take a look at the bigger picture, which is the cascade, the CSS cascade itself.

Now I could give you the formal definition there from the spec, but it's a bit boring and a bit difficult because here I have a more simple definition namely this one.

The CSS cascade is the algorithm that determines the winner from a group of competing declarations.

Now, if that's also confusing for you, no worries, here's a small example to clarify.

Here, we have this simple, a little bit of markup-input type is password, and we have an element attached they're attached to it, namely color blue.

But in our style sheet, we also have three CSS rules that also want to set the color.

So these are all competing, the declarations, the core declarations, they are all competing with each other.

And that was the question which one of these rules will win.

Is it the first one, the last one, the loudest one?

Well, that's what the CSS cascade does.

It gives us an answer.

It's picks a winner from these competing declarations and it does this using these five steps.

Now there's this without taking cascade layers into account just yet.

So cascade layers later on will fit there somewhere inside of the scheme, but these are the five steps without layers.

Nd what the cascade does, well, it checks each and every one of those layers, one after the other, until one of those steps can determine the winner.

For example, if specificity can determine the winner, then the cascade will say, okay, well, here's your winner.

This declaration wins.

And I won't check any of the other steps.

And this is by the way, in the same way how a waterfall works, because we see a lot of these photos here from ... from cascade, from waterfall.

If you take a look at the water in the center, there, it takes five steps before it hits the bottom, before it hits the lower level of the water.

But if you look at the right hand side of the photo, well, there the water only takes one step before it hits the bottom.

So our CSS cascade works in a similar way.

It can, it can take one step to determine the winner, or it can take five steps to determine the winner.

And later with layers, even six steps.

Now let's, let's take a look at the steps themselves, because what, what are they?

The first step that the cascade checks well that's origin and importance.

The specification's pretty straightforward on this.

It says, well, the origin of declaration is where it come from, where it comes from.

And it's importance.

If we take a look at the different origins that we have in the browsers, we have eight and we can rank these.

We can rank these from lowest priority at the bottom to highest priority at the top.

And the specification says, well, CSS rule sets in a higher priority origin, those will win from rule sets in a lower priority origin.

Pretty straightforward.

But what are these origins?

Well, to start off, we have three origins sitting at the bottom there-the normal user agent declarations, the normal user declarations and the normal author declarations.

Normal user declarations, those are the styles that the browser brings with it.

So these are the styles provided by your browser.

Think of the white background, text is Times New Roman, Text is black, links are blue.

Text is 16 pixels by default.

That kind of stuff.

Well, that's all in there.

On top of that, we have the normal user declarations.

This is an origin that contains preferences from the user of the browser.

So that's actually the person sitting at the computer and using the browser.

For example, if somebody is visually impaired, well they can enter a setting and say, you know what?

I don't want these 16 pixels by default text size.

I want it to be bigger.

I want it to be 32 pixels so they can set it there.

And what they set there, well, that will always win from the normal user declarations that sits below because normal use declarations has a lower priority.

And then the third layer on top of that is our normal author declarations.

What are authors?

Well, 'author' is spec lingo for a CSS developer.

So there go the styles that we as CSS developer define, and these will override the user declarations and also override their normal user agent declarations.

You can see this in the dev tools, by the way.

So if, look at a form there I have to input type is 'search'.

And we inspect it using the dev tools, then at the bottom there in the styles panel, we can see which styles get applied, which declarations get applied.

Now, let me make it a little bit bigger there.

We see clearer here at the top we have a padding set by four pixels, but then it's the right hand side.

It says, well, this is from this specific style sheet.

And then below that we have a few other styles, which I didn't write.

No, because these are part of a user agent style sheet.

And it's also mentioned here on the right.

So they are ... visually stacked on top of each other which is correct and confirms what I just told you.

Now, something interesting happens when you use '!Important'.

You may think, well in inside of these three origins, if I use an !Important it will make something important inside of the one origin?

No, that's not the case.

What happens here is we take our three origins that we have now and we stack them in reverse, on top, like this.

So at the bottom normal user agent, normal user, normal author, and then we have !Important author, !Important user and !Important user agent.

And this, this kind of makes sense because if a user really wanted 32 pixels font size, they can add an !Important to it.

And then their 32 pixels, which is made important at that moment, it will win from our from our own important order declarations.

And to finalize, we have two other origins namely the transition origin at the top, and then in the middle there, right between the normal ones and the important ones that we have animation declarations.

If you take a look at an example, well, here we have this.

We have all these CSS rules and these declarations that are competing, which one will win?

Well in the first step, the cascade will take a look at the origins.

And here we see that the input type is password that selector there, the hot pink has an !Important added to it.

So this is from an origin with higher, with a higher priority than our other ones.

So the hot pink !Important is part of the !Important author declarations and all the other declarations that you see there, these are part of the normal author declarations.

Cool.

Right.

So the next step is context.

This has to do with web components and shadow DOM.

And what the specs say there, well normal rules, these declarations, from our context, these will win from inner context.

And then for important rules, it goes the other way, round to the ones from the inner context will win from those from the outer context.

Say our CSS rules are part of the same context, then we move on to element attached styles.

element attached styles it's very simply translated to the contents of a style attribute.

These will win from normal style declarations.

So here again, we have a small example.

At the top, there we have the element attached style.

Namely color is blue.

Well, since it's an element attached style, it will win from our other declarations that we see there from the other CSS rules.

So the text will actually be blue.

Moving on to the next step.

Specificity, it's pretty straightforward.

The declaration with the high specificity wins.

But how do you calculate specificity?

Well, that, that's, that's another question, of course.

But let's just jump into, into an example.

Here we have several declarations, again, part of the same origin, part of the same context, no element attached style is being used.

So we have to look at specificity.

We calculate the specificity for each and every one of those selectors.

And then we see, well, aha, the ID selector there.

This one has the highest specificity, so we, we choose that one from the list.

If you need a little bit of help with calculating specificity, there is a wonderful tool made by Kilian Valkhof, and he will be speaking later today.

And there's wonderful tool by Kilian Valkhof, which is the CSS specificity calculator.

You paste in a selector in there and out there at the bottom comes the specificity.

Not only does it give you the specificity, it also explains you why you have reached a certain specificity.

Highly interesting tool, I would really recommend it to you.

If you want to calculate specificity from it in your own coat, that's also possible for this, you can use a package that I made.

It's named @bramus/specificity.

You import the calculate function from it.

You pass a selector into there and out comes the specificity.

Really handy.

If you want to do it from it within your code.

Now, bringing you back through to our grand scheme of things.

Say we have CSS rules in the same origin, same context, no element attached styles, and then the specificity of a selectors is the same.

What do we do then?

Well, then we check the last and final step namely order of appearance and it goes like this.

Well, the last declaration in the document order wins, Again a little example.

All in the same context here.

So we pick the last one and the color will be lime, yay.

So these are five steps of the cascade.

I hope I made it a little bit clearer to you.

But now let's move on to layers, of course, because this is a talk about cascade layers . If we take a look back again at our scheme.

Well, if we're really honest we're like mostly fiddling with these two steps, because most of the time our CSS rules are part of the same origin, have the same importance, same context, no element attached styles.

And then we are like battling with either specificity or order of appearance and we can run into some problems.

For example, say I have this here in my CSS reset style sheet.

Very nice.

It will remove the margin and padding and the list style from UL, which has a class attached to it.

Good, nice.

But then if I want to like set the margin on my NAV, which is also a UL, then I have a problem.

It won't be applied though, because the specificity from the topmost selector, it has a higher specificity than the selected there at the bottom.

Uh, A work around that we can use for this is we can kind of cheat by bumping up the specificity so that it has the same specificity.

And then the next step in the cascade, which is order of appearance will come into play and pick the, the last declaration that you see here.

Yup.

Not that nice, but yeah, it's a solution.

Another example.

This is from a carousel that you can use.

So it's a third party style sheet say we want to adjust the slider item.

Well, again, we have a problem there.

We could try and bump up the specificity or try to give it like a higher specificity or what we mostly do in those cases, we add an !Important on there, and then it becomes part of a different origin, a more higher priority origin.

And then it will win that way.

Not always feasible because if we want to like override it again later on, well then we're running into problems.

Another cool, a cool example is say you declare a card_content element in your markup.

So you have div class is card_content, for example, but you also add the u-text-center utility class on there.

Well, the text will always be left aligned.

That is because the card_content selector in our style sheet, comes later than the u-text-center utility class that we have there.

And since they have both specificity, order of declaration is the determining factor.

As olution that we can do there as well, we can move the content of our style sheet.

But again, this is not always something that we can do or are allowed to do, especially in larger code bases.

We can, we can run into problems.

So we just kind of confirmed that well we're mostly fighting with specificity and order of appearance.

What if you didn't need to, what if there was an extra step inside of the cascade that would determine the winner for us and without us needing to fall back to specificity or order of appearance?

Well, that's exactly what layers do because it's this extra step inside of the cascade and if the layer step can determine the winner.

Well, you don't have to worry about specificity or order of appearance.

Pretty great, right?

What, what can you, what can you do with cascade layers?

With cascade layers, you get to slice up your single origin styles into several layers and control the priority of each and every one of those layers.

And you do this using the @layer or at-rule.

Bringing back to our example, the one with the reset that was over-riding things: with layers we can solve this like this.

We wrap both of these in a separate layer and it will work magically.

How come?

Well, the spec says cascade layers are ordered by order of appearance, and then they are given a certain priority.

Visualized, it looks like they say we have four layers declared.

So on the left here, we have the declaration order.

First in our style sheet comes @layer reset with some contents and @layer base, @layer components, and finally @layer utilities.

The cascades will sort them by order of appearance, which you can see on the left, but the priority is not the same as the order of appearance.

No, it's the reverse, because the first declared layer, that one gets the lowest priority.

And the layer declared after that gets a bit of a higher priority.

And so forth.

And then finally the last declared layer, that one has the highest priority.

And then the spec says, well, for normal rules, the cascade layer with the highest priority wins.

So if you take a look again at this example, in a reset layer, we have that little bit of a reset styles.

And then in the components layer, we have the nav specific styles.

Well, they will win.

Document declaration order is reset, base, components, utilities, but then the priority order is reversed.

Reset has the lowest priority, and then components, the components layer has a higher priority.

So it will win.

Yay.

Pretty cool, right?

I think so.

These are the basic concept of layers.

But of course, as with everything, there's more to say about it because there are some edge cases, some, some finesses to, to include.

So let's get, let's go over them.

Layer name reuse, looking again at the spec at the spec lingo, it says the cascade layers are sorted by the order, by which they are declared.

That means that if you reuse a layer name, you won't declare it again.

What will happen there is the contents of both of these @layer statements, using the same layer name, they will get merged.

So you can also write the thing there on the, on the right.

So the code snippet there on the left and the code snippet on the right, they are equivalent.

Anonymous layers.

That's another finesse of the spec.

You don't need to give a leader a name so you can do this: @layer, no name and just the contents you put them in there.

One caveat with that you can't append to an anonymous layer because these two statements here, they won't merge the contents, no these will create two separate anonymous layers.

Predefining Layer Order.

That's something else.

It's something that I would highly recommend you to do.

Because if, if you take a look at our style sheet, with our several layers in, and then we have the priority order on the right.

If we move some of these layers in our declaration order.

Well, then our priority or also changes.

And that's [not] something, not something that we always want.

We can solve this by using the @layer statement.

And with the @layer statement, we can predefine layer order upfront.

It looks like this is.

This little extra rule that you put at the top of your style sheet-@layer, and then you name your layers, one after the other.

Reset, base, components and utilities.

So we declare them there right upfront.

And that way, if we move one of our layers, well, our priority order will always remain the same because we have predefined it.

I think that's, that's a really neat and a really handy thing to do.

I would really highly recommend you to do it.

If you're wondering about external CSS files, can you load, can you load these into layers?

Yes, you can.

If you're using @import, you can add an extra layer function to here.

So they're here.

In this example, we have predefined layer order at the top, and then we load in all of these external CSS files into their layer, using its layer name.

And then at the bottom we can write our own style sheets again in layers, if you want.

Can you do this for markup?

Yes.

You can also do it from mark.

You do it using the layer attribute.

So you have link rel style sheet, href, name of the CSS file and then layer equals reset.

But that won't work entirely because older browsers that don't support layers, well, they, they will discard the layer attribute there.

So, so browser don't support layer attribute, they will discard the contents there and they will load those style sheets not in a layer.

How can you solve this?

Well, there's this extension to the media attribute that you can do, and there you can detect support for the @layer, at-rule.

So this at-rule, there it's a new function.

It's not entirely finalized at this moment, but it's, it's, it's being worked on.

So use the little code snippet from the bottom.

That one will work definitely.

Unlayered Styles.

Do you need to use layers?

Are you forced to use layers?

No, you can still write styles without using any layers.

But the question then is, well, where do they fit in the order?

Did they get the highest priority or the lowest priority.

Well, unlayered styles, these always get the highest priority.

So no matter where you have your unlayered styles, unlayered styles, the will always win from layered styles.

And that's, that's kind of a cool thing to know, that they will always be.

Layer nesting.

It's possible to nest layers.

You have an @layerblock.

And in there you put some other @layers, @layer blocks.

For example, here inside of our these, you have light and dark, and also some unlayered styles that perfectly works.

If you want to later on append content to a nested layer, you use the dot notation.

So if you are used to writing JavaScript, this will look really familiar referring to the dark layer inside of the theme layer.

!Important.

oooh, remember the thing from origins, where like the priority stacked in reverse on top?

Well, with cascade layers, we have the same.

If we use an !Important inside in one of our layers, then, well, the order will get stacked in reverse on top.

So yes, you're unlayered styles will always win from layered styles, but also yes, !Important, which is being used inside of a layer, these will always win from the unlayered style.

And then rolling back layers.

Just like how you can roll back cascade origin using the revert keyword, you can do the same in cascade layers, but then you need the revert-layer keyword.

So here, if we have an a.normal somewhere in our markup, it won't be colored hot pink, no, it will be, it will have applied color revert layer.

So it will fall back to the previous layer.

Which is the base layer, which says, well, the core will be lime.

So a.normal, those elements will have the lime color, and then the regular a's, well these eventually will have the hot pink color.

And one thing too, to close off it, if you're wondering about browser support can you use them well?

Yes, you can.

It's supported in all major browsers.

So all of these versions, they should be released around this time when, when this is being aired.

So Chrome 99 chromium 99, I should say.

So that's the engine that sits underneath Google Chrome Microsoft Edge Brave and all kinds of things.

They all use the blink renderer.

They're all based on chromium.

And then Gecko Firefox.

They have shipped it with version 97, which was from early February.

And then Safari has it enabled behind a flag in Safari technology preview 133 and it will, it will ship with version 15.4.

Now, what about old browsers do they support it?

Well old browsers, they look at your style sheet and they go like, yeah, I don't understand this layer thing and what they will ... essentially do there is well, they will discard the entire content so they won't apply any layered styles.

That can be a bit of a problem, cause it, you could be missing some essential styles there.

How can we solve this?

Well, if you could use a preprocessor of some kind to preprocess the contents, then we would be able to use it.

It would end up with a style sheet like this.

So all the styles would be processed, but again, would become un-levered.

There are two projects that are working on it.

You can see the links onscreen here, but they are not finalized yet.

So, so we still have to wait a little bit before they get shipped , these these converters that we can use.

So, yeah.

Wow.

That was a lot of information.

In summary.

Cascade layers, it's this extra step that sits in the cascade right before specificity and order of appearance.

That way, if the layers step from the cascade can determine the winner that we don't need to worry about specificity or order of appearance of the rules that are of the selectors that are used in the other layers.

They have a declaration order from top to bottom, but then the priority order, well, it's in reverse and know that unlayered styles always sit at the top.

So unlayered styles have the highest priority.

If you're looking for like a practical approach, how would you do this?

Or how will you migrate from a non layered style sheet to a layered style sheet?

Well, this here is our a non layered style sheet.

So we start off with this.

What I would recommend you to do is first start off with loading up your reset styles and your third-party styles into layers.

Like this.

So we have our layer statement there at the top, reset and third-party, and then we load those style sheets into their proper layers.

And then we keep our own styles unlayered, because again, unlayered styles will always win from layered styles.

Cool.

Should your codebase become bigger and bigger and bigger?

And you're working towards approaches, for example, like ITCSS, well, then you can start layering up your own styles and it would look like, like this.

And that was it.

I want to thank you for your attention.

If you have any questions, try and look me up in the chat right now.

I should be there.

But perhaps I won't be due to time zone things like that, and otherwise try and reach me on Twitter.

My name is Bramus.

I work from Belgium.

I also run a bram.us, that's also on Twitter.

Finally here, this is the link to an article that I wrote regarding cascade layers.

So if you're, if you want to read up on it again in your own time that you can do it right there.

Thank you.

Cascade Layers

@bramus
2022.04 — Web Directions Hover
https://www.bram.us/

Cascade Layers

The Cascade

https://www.w3.org/TR/css-cascade-5/

The CSS Cascade is the algorithm that determines the winner from a group of competing declarations

<input type="password" id="password" style="color: blue;" />

input { color: grey; }
input[type="password"] { color: hotpink !important; }
#password { color: lime; }

(* Without Layers)

  • A box labelled Origin and Importance
  • arrow labelled "Same Origin" points to
  • A box labelled Context
  • arrow labelled "Same Context" points to
  • A box labelled Element Attached Styles
  • arrow labelled "Not via style attr" points to
  • A box labelled Specificity
  • arrow labelled "Same Specificity" points to
  • A box labelled Order of Appearance

The diagram above, with the first box, labelled "Origin and Importance" colored yellow

The diagram above, with the second box, labelled "Context" colored yellow

The diagram above, with the third box, labelled "Element Attached Styles" colored yellow

The diagram above, with the fourth box, labelled "Specificity" colored yellow

The diagram above, with the fourth box, labelled "Specificity" colored yellow. There is a crown on top of the box now

Image of a waterfall

Timo Volz — https://unsplash.com/photos/nwixMbLJy8E

Again the diagram with the boxes and arrows, none of the boxes are colored

The diagram above, with the first box, labelled "Origin and Importance" colored yellow

The text "The origin of a declaration is based on where it comes from and its importance" appears in a yellow box. An arrow points at another box labelled "context"

A yellow box with a 8 smaller boxes inside, about the width of the containing box stacked one above the other fill that container. As before an arrow points at another box labelled "context"

This diagram is repeated, with the text "Highest priority" at the top left of the container and "Lowest Priority" at the bottom left. An arrow pints from the lower text to the higher.

This diagram is repeated, with the text "Normal user agent declarations" inside the bottom box inside the container.

This diagram is repeated, with the text "Normal user declarations" inside the box second from the bottom inside the container.

This diagram is repeated, with the text "Normal author declarations" inside the box third from the bottom inside the container.

Screenshot of a browser window with developer tools open to the right. The focus is on a search field.

Zoom in on the style pane of the dev tools with the CSS below

input {
	padding: 4px;
}

input [type=" search" i] {
	appearance: auto;
	box-sizing: border-box;
	padding: 1px 2px;
}

!important

Repeat of the most recent diagram, with the text "Normal author declarations" inside the box third from the bottom inside the container.

This diagram is repeated, the box fourth from the bottom empty, then the three boxes above that labelled "Important author declarations", "Important user declarations" and "Important user agent declarations" in ascending order.

Repeat of the diagram, with text in each of the boxes inside the container, as below, from top to bottom

  • Transition declarations
  • Important user agent declarations
  • Important user declarations
  • Important author declarations
  • Animation declarations
  • Normal author declarations
  • Normal user declarations
  • Normal user agent declarations
<input type="password" id="password" style="color: blue;" />
input { color: grey; }
input[type="password"] { color: hotpink !important; }
#password { color: lime; }
<input type="password" id="password" style="color: blue;" />
input { color: grey; }
input[type="password"] { color: hotpink !important; }
#password { color: lime; }

The diagram from earlier of several boxes connect by arrows in sequence, with the second box, labelled "Context" colored yellow

For normal rules the declaration from the outer context wins

and for important rules the declaration from the inner context wins.

The diagram above, with the third box, labelled "Element Attached Styles" colored yellow

Declarations such as the contents 
 of a style attribute take precedence over declarations mapped via style rule.

<input type="password" id="password" style="color: blue;" />
input { color: grey; }
input[type="password"] { color: hotpink; }
#password { color: lime; }
<input type="password" id="password" style="color: blue;" />
input { color: grey; }
input[type="password"] { color: hotpink; }
#password { color: lime; }

The diagram above, with the fourth box, labelled "Specificity" colored yellow

The declaration with the highest specificity wins

<input type="password" id="password"/>
input { color: grey; }
#password { color: lime; }
input[type="password"] { color: hotpink; }
<input type="password" id="password"/>
input { color: grey; }                      /* (0,0,1) */
#password { color: lime; }                  /* (1,0,0) */
input[type="password"] { color: hotpink; }  /* (0,1,1) */
<input type="password" id="password"/>
input { color: grey; }                      /* (0,0,1) */
#password { color: lime; }                  /* (1,0,0) */
input[type="password"] { color: hotpink; }  /* (0,1,1) */

Screenshot of the Polypane Specificity Calculator

https://polypane.app/css-specificity-calculator/

Screenshot of the Specificity Utility by Bramus on NPM.

https://brm.us/specificity

The diagram from earlier, with the fifth box, labelled "Order of Appearance" colored yellow.

The last declaration in document order wins

<input type="password" id="password" />
.password input {
	color: grey;
	color: hotpink;
}

.password input {
	color: lime;
}
<input type="password" id="password" />
.password input {
	color: grey;
	color: hotpink;
}

.password input {
	color: lime;
}

The original diagram of boxes joined by arrows.

Introducing
 Cascade Layers

The original diagram of boxes joined by arrows.

The original diagram of boxes joined by arrows. The final two boxes, labelled "specificity" and "order of appearance" are colored.

ul[class] {
	margin: 0;
	padding: 0;
	list-style-type: none;
}
ul[class] {
	margin: 0;
	padding: 0;
	list-style-type: none;
}
 
.nav {
	margin: 0 40px;
}
ul[class] { /* (0,1,1) */
	margin: 0;
	padding: 0;
	list-style-type: none;
}
 
.nav { /* (0,1,0) */
	margin: 0 40px;
}
ul[class] { /* (0,1,1) */
	margin: 0;
	padding: 0;
	list-style-type: none;
}
 
.nav { /* (0,1,0) */
	margin: 0 40px;
}
.slider.slider-horizontal > .slider-item {
	background: #fefefe;
	padding: 10px;
	border: 1px solid #000;
}
.slider.slider-horizontal > .slider-item {
	background: #fefefe;
	padding: 10px;
	border: 1px solid #000;
}
 .slider-item {
 	border-color: #F00;
}
.slider.slider-horizontal > .slider-item {
	background: #fefefe;
	padding: 10px;
	border: 1px solid #000;
}

 .slider-item {
	border-color: #F00 !important;
 }
.u-text-left {
    text-align: left;
}
.u-text-center {
    text-align: center;
}
.u-text-right {
    text-align: right;
}
.card__content {
    text-align: left;
}
.card__content {
    text-align: left;
}
.u-text-left {
    text-align: left;
}
.u-text-center {
    text-align: center;
}
.u-text-right {
    text-align: right;
}

The original diagram of boxes joined by arrows.

The diagram above, with the fourth box, labelled "Layers" colored yellow

With Cascade Layers you get to slice up single-origin styles in several layers, and control the priority of each layer

@layer

 ul[class] {
    margin: 0;
	padding: 0;
    list-style-type: none;
}

.nav {
    margin: 0 40px;
}
@layer reset {
	ul[class] {
		margin: 0;
		padding: 0;
		list-style-type: none;
	} 
	}

@layer components {
    .nav {
        margin: 0 40px;
    }
}
Cascade layers are ordered by order of appearance [and prioritized]

Two rectangles are joined by an arrow. Above the recatangle on the left is the text "Cascade Layers (Declaration Order)". This rectangle on the left contains 4 rectangles, one on top of ech other. From top to bottom they contain text @layer reset{…}, @layer base{…}, @layer components{…}, @layer utlities{…}

Above the second rectangle is the text "Cascade Layers (Priority Order, Stack)". This rectangle contains a ?. To its right at the top are the words "Highest Priority", and to its bottom right the words "Lowest Priority". An arrow points from bottom right to top right

The same diagram as before. Now in the second rectangle in place of the "?" there's a rectangle filling the bottom quarter, containing the text "@layer reset {…}"

The same diagram as before. Now in the second rectangle there's an additional rectangle above the one filling the bottom quarter, containing the text "@layer base {…}"

The same diagram as before. Now in the second rectangle there's an additional rectangle above the previous containing the text "@layer components {…}"

The same diagram as before. Now in the second rectangle there's an additional rectangle above the previous containing the text "@layer utilities {…}"

For normal rules the cascade layer with the highest priority wins

@layer reset {
	ul[class] {
		margin: 0;
		padding: 0;
		list-style-type: none;
	}
}

@layer base {
	…
}

@layer components {
	. nav { /* >ul< */
		margin: 0 40px;
	}
	
@layer utilities {
	…
}
@layer reset {
	ul[class] {
		margin: 0;
		padding: 0;
		list-style-type: none;
	}
}

@layer base {
	…
}

@layer components {
	. nav { /* >ul< */
		margin: 0 40px;
	}
	
@layer utilities {
	…
}

🤩

Cascade Layers In-Depth

Layer Name Reuse

Cascade layers are sorted by the order in which they first are declared

@layer base {
	…
}
@layer components {
	.button {
		…
	}
}

@layer utilities {
	.text-left {
		…
	}
	
@layer components {
	. card {
		…
	}
}

Code as before, but orange boxes surround the two blocks below

@layer components {
	.button {
		…
	}
}
	
@layer components {
	. card {
		…
	}
}

Code from above but with the two highlighted blocks merged to make this one block

@layer components {
	.button {
		…
	}

	.card {
		…
	}
}

Anonymous Layers

@layer {
  	…
}
@layer {
	…
}

@layer { /* Won't append! */
	…
}

Predefine Layer Order

Diagram from earlier with two rectangles, labelled "Cascade Layers (Declaration Order)" and "Cascade Layers (Priority Order, Stack)". There are now 5 boxes inside the container on the left, labeled from top to bottom as follows

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer utilities { ... }

In the container on the right there are still 4 boxes labeled from top to bottom as follows

  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

Hoever the @layer base box is twice the height of the others.

The same diagram as above with the order of the boxes in each container changed. On the left the order is

  • @layer reset { ... }
  • @layer base { ... }
  • @layer utilities { ... }
  • @layer base { ... }
  • @layer components { ...

on the right it is

  • @layer components { ... }
  • @layer utilities { ... }
  • @layer base { ... }
  • @layer reset { ... }

Again the same diagram as above with the order of the boxes in each container changed once more. On the left the order is

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer utilities { ... }

on the right it is

  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

@layer 
 Statement

Again the same diagram as above. Inside the container on the left at the top is the following text "@layer reset, base, components, utilities;". The order of the boxes in each container on the left is

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer utilities { ... }

and on the right it is

  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

The diagram as above with one change. The order of the boxes in the left hand container is now

  • @layer reset { ... }
  • @layer base { ... }
  • @layer utilities { ... }
  • @layer base { ... }
  • @layer components { ... }

Again, the diagram as above with one change. The order of the boxes in the left hand container is now

  • @layer reset { ... }
  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer base { ... }

and again, the diagram as above with one change. The order of the boxes in the left hand container is now

  • @layer base { ... }
  • @layer utilities { ... }
  • @layer components { ... }
  • @layer reset { ... }
  • @layer base { ... }

External CSS Files

@layer reset, thirdparty, base, components, utilities;

@import url(reset.css) layer(reset);
@import url(carousel.css) layer(thirdparty);
@import url(map.css) layer(thirdparty);

@layer base {
	…
}
❌  <link	rel="stylesheet"
  			href="reset.css"
 			layer="reset" />

✅  <link	rel="stylesheet"
 			href="reset.css"
			layer="reset"
			media="supports(at-rule(@layer))" />

Unlayered Styles

A return to our original diagram from before. Two rectangles are joined by an arrow. Above the recatangle on the left is the text "Cascade Layers (Declaration Order)". Inside this rectangle, at the top is the text "@layer reset, base, components, utilities;". This rectangle on the left also contains 5 rectangles, one on top of each other. From top to bottom they contain following text

  • @layer reset { ... }
  • @layer components { ... }
  • @layer base { ... }
  • (Unlayered) (Unlayered)
  • @layer utilities { ... }

Above the second rectangle is the text "Cascade Layers (Priority Order, Stack)". This rectangle contains a ?. To its right at the top are the words "Highest Priority", and to its bottom right the words "Lowest Priority". An arrow points from bottom right to top right

The diagram is repeated. This time the container on the right contains equal sized rectangles labelled with the following text (from top to bottom)

  • (Unlayered)
  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

The diagram is repeated with the order of the boxes inside the left hand container changed to

  • @layer reset { ... }
  • (Unlayered)
  • @layer components { ... }
  • @layer base { ... }
  • @layer utilities { ... }

The diagram is again repeated with the order of the boxes inside the left hand container changed to

  • @layer reset { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer utilities { ... }
  • (Unlayered)

Layer Nesting

@layer theme {
	@layer light {
		root {
			--primary-color: #000;
		}
	}

	@layer dark {
		: root {
			--primary-color: #fff;
		}
	}

	h1 {
		font-family: Helvetica;
	}
}
}
@layer theme.dark {
	:root {
		--contrast-color: #333;
	}
}

!important

Our diagramwith two rectangles repeated. Text order on the left hand side is

  • @layer reset { ... }
  • @layer components { ... }
  • @layer base { ... }
  • Unlayered
  • @layer utilities { ... }

on the right hand side it is

  • Unlayered
  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

Our diagram is again repeated. Mo the following 10 boxes appear in the left hand rectangle

  • Important @layer reset { ... }
  • Important @layer base { ... }
  • Important @layer components { ... }
  • Important @layer utilities { ... }
  • Important Unlayered
  • Unlayered
  • @layer utilities { ... }
  • @layer components { ... }
  • @layer base { ... }
  • @layer reset { ... }

Rolling back Layers

@layer base {
	a {
		color: lime;
	}
}
@layer theme {
	a {
		color: hotpink;
	}

	a. normal {
		color: revert-layer;
	}
}

Browser Support

Edge 96 99
Firefox 94 97
Safari TP133 15.4

Older icons for Chrome, Firefox and Safariwith red crosses under each.

Older icons for Chrome, Firefox and Safariwith red crosses under each. Below is listed

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer utilities { ... }
  • (Unlayered)

Older icons for Chrome, Firefox and Safariwith red crosses under each. Below is listed

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer utilities { ... }
  • (Unlayered)

Each has a red cross to the right, except "Unlayered" with a green tick.

The above is repeated, withall but "unlayered" in a trash can icon.

Older icons for Chrome, Firefox and Safariwith red crosses under each. Below is listed

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer utilities { ... }
  • (Unlayered)

Older icons for Chrome, Firefox and Safari with green recycle icons under each. Below is listed

  • @layer reset { ... }
  • @layer base { ... }
  • @layer components { ... }
  • @layer utilities { ... }
  • (Unlayered)

All but "unlayered" have recycle icons to their right. "Unlayered" has a green tick

Older icons for Chrome, Firefox and Safari with green recycle icons under each. Below is listed

  • Processed, Unlayered reset
  • Processed, Unlayered base
  • Processed, Unlayered components
  • Processed, Unlayered utilities
  • (unlayered)

All but "unlayered" have recycle icons to their right. "Unlayered" has a green tick

Summary

Our earlier diagram of the 6 boxes connected by labelled arrows. The box labelled "layers" is colored yellow and wears a crown.

The earlier diagram of two rectangles containing boxes that are labelled.

Practical

@import url(reset.css);
@import url(carousel.css);
@import url(map.css);
main {
	…
}
@layer reset, thirdparty;

@import url(reset.css);
@import url(carousel.css);
@import url(map.css);
main {
	…
}
@layer reset, thirdparty, base, components, utilities;

@import url(reset.css) layer(reset);
@import url(carousel.css) layer(thirdparty);
@import url(map.css) layer(thirdparty);

@layer base {
	…
}

https://brm.us/cascade-layers

Thanks / Questions