Questions From The New Cascade
Tags: css
Published: 2022-03-05 02:28:31

Cascade layers is probably the biggest upgrade the CSS cascade has had since its inception. Now that Chrome has joined Firefox in having cascade layers enabled by default, and there's various articles available describing them, it's time to start thinking about how to use them. Have you grokked them yet?

To test yourself, here's three small questions about them, plus explanations of the answers. Let's get started.

<h2>Question One</h2>
What colours are used for the foreground text and the page background in the following

```html
<!DOCTYPE html>
<html lang="en-gb">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Question One</title>
  <style>@import url("data:text/css, \
    body.myBody { \
       background-color:blue; \
       font-size:4em; \
    } \
    * { \
       color:yellow !important; \
    } \
    ") layer; 
  </style>
  <style>
    body { 
      background-color: red;
      color: white !important;
    }
  </style>
</head>
<body class="myBody">
Hello World
</body>
</html>
```
[Codepen with answer](https://codepen.io/alohci/pen/qBVggBm)
(Link, because it seems dev.to doesn't support embedding a codepen within a disclosure section.)

{% spoiler Reveal Answer Explanation %}

The text colour is yellow and the background colour is red.

The first thing to understand is the @import. Normally, this would link to an external style sheet, but here for the benefit of having everything in one place for demonstration purposes, I've used a data url. 

The "layer" keyword at the end tells the browser to place all the contents of the imported resource into a layer. The layer is not named, so it is an "anonymous" layer. Anonymous layers work like named ones, except they can't be referenced so once defined they can't be added to, and they can't be manipulated in the layer order.

So in this case, the @import is exactly equivalent to
```css
@layer {
    body.myBody {
       background-color:blue;
       font-size:4em;
    }
    * {
       color:yellow !important;
    }
}
```

Taking the background colour first where all the declarations are non-important, if the layer was not involved and specificity was the determining factor, the `body.myBody { background-color:blue; }` rule would beat the `body { background-color: red; }` rule and the background would be blue. 

But for non-important declarations, rules in a layer are always lower precedence than rules not in a layer, so the `body { background-color: red; }` rule wins.

For `!important` declarations like the text colour, the opposite applies. Layered rules take precedence over unlayered rules regardless of specificity, so the `* { color:yellow !important; }` rule wins.
{% endspoiler %}

<h2>Question Two</h2>

This question looks similar. The unlayered styles have been moved to be inline styles of the body element. Again, what are the final text and background colours? Is it the same answer?

```html
<!DOCTYPE html>
<html lang="en-gb">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Question Two</title>
  <style>@import url("data:text/css, \
    body.myBody { \
       background-color:blue; \
       font-size:4em; \
    } \
    * { \
       color:yellow !important; \
    } \
    ") layer; 
  </style>
</head>
<body class="myBody" style="background-color:red;color:white!important">
Hello World
</body>
</html>
```

[Codepen with answer](https://codepen.io/alohci/pen/YzEBMJx)
{% spoiler Reveal Answer Explanation %}

The background is still red, but now the text color is white.

In the answer to question one, I explain that `!important` layered styles have precedence over `!important` unlayered styles regardless of specificity. Yet here the inline style wins.

In CSS 2, inline styles were unambiguously part of specificity. There any innumerous guides to specificity that will tell you that.

Yet in CSS 3 until now, the relationship between inline styles and specificity has had a distinctly ambiguous air. The specifications have neither wholly placed inline styles as part of specificity, nor provided any alternative.

With Cascade layers that ambiguity has finally been removed. Inline styles are no longer part of specificity! Instead they have their own precedence level in the cascade called "Element-Attached Styles". Element-Attached Styles have a precedence which is above both cascade layers and specificity. So an Element-Attached style wins over layered styles even when `!important` is used on both. 
{% endspoiler %}

<h2>Question Three</h2>
Something different now. What are the rendered sizes of the three images? (For those not familiar with placeholder.com, the image supplied from http://via.placeholder.com/100 will have intrinsic dimensions of 100px x 100px.)

```html
<!DOCTYPE html>
<html lang="en-gb">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Question Three</title>
  <style>
    img { 
      height: 200px; 
      width:200px; 
    }
    .beta { 
      height: revert; 
      width:revert; 
    } 
    .gamma { 
      height: revert-layer; 
      width:revert-layer; 
    } 
  </style>
</head>
<body>
  <img 
    class="alpha" 
    src="https://via.placeholder.com/100"
    height="150" width="150" 
    alt="100 pixel square placeholder">
  <img 
    class="beta" 
    src="https://via.placeholder.com/100"
    height="150" width="150" 
    alt="100 pixel square placeholder">
  <img 
    class="gamma" 
    src="https://via.placeholder.com/100"
    height="150" width="150" 
    alt="100 pixel square placeholder">
</body>
</html>
```
[Codepen with answer](https://codepen.io/alohci/pen/ExbrBBJ)
{% spoiler Reveal Answer Explanation %} 

The image sizes are 200px x 200px, 100px x 100px & 150px x 150px respectively.

Image alpha is 200px x 200px. Until the introduction of cascade layers, the values of the HTML height and width attributes were treated as presentational hints at the start of the author origin style sheets with a specificity of 0, so any matching CSS rules would take precedence over them, and the HTML dimensions are only a resort to be used if CSS doesn't provide any. So the CSS provided dimensions win.

Image beta is 100px x 100px. Introducing cascade layers produces a slight problem though. Because non-important layered styles have lower precedence that unlayered styles, if the HTML attribute presentational hints were treated as unlayered styles, then they'd have precedence over any CSS in a layer. That not ideal. So a new origin has been created for them called the "author presentational hint origin". This has precedence lower than all layered and unlayered styles. 

However, the "revert" keyword has well established behaviour. When effective in an author origin style sheet, it will revert the author origin matching styles including the presentational hints. So to maintain that behaviour, "revert" has been slightly redefined to include the reverting of the "author presentational hint origin".

As a result, the "revert" has the effect of taking the size of the image back to its intrinsic dimensions.

Image gamma is 150px x 150px. You might think that as there don't appear to be any cascade layers in use in the CSS, that "revert-layer" would either do nothing at all, or do the same as "revert". In fact, since it's in an unlayered style, it reverts the unlayered styles of the image dimensions. 

But it is free from any backward-compatibility restrictions, so it doesn't revert the styles of the "author presentational hint origin", and so the image is displayed using the dimensions taken from the HTML attributes. 
{% endspoiler %}