DEV Community

Nicholas Stimpson
Nicholas Stimpson

Posted on

Questions From The New Cascade

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.

Question One

What colours are used for the foreground text and the page background in the following

<!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>
Enter fullscreen mode Exit fullscreen mode

Codepen with answer
(Link, because it seems dev.to doesn't support embedding a codepen within a disclosure section.)

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

@layer {
    body.myBody {
       background-color:blue;
       font-size:4em;
    }
    * {
       color:yellow !important;
    }
}
Enter fullscreen mode Exit fullscreen mode

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.


Question Two

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?

<!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>
Enter fullscreen mode Exit fullscreen mode

Codepen with answer

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.


Question Three

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.)

<!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>
Enter fullscreen mode Exit fullscreen mode

Codepen with answer

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.


Top comments (0)