htmlcssstacking-context

Why is the body tag a stacking context, depending on the background color of the html tag


Every HTML page has a stacking context at the root element (htmltag).

In the following example the html tag is the (only) stacking context

html {
background-color: gray;
}
body {
background: #222;
color: white;
box-sizing: border-box;
}
.box {
width: 200px;
height: 200px;
background-color: blue;
position: relative;
}
.box:before {
content: "";
background-color: tomato;
position: absolute;
z-index: -1;
inset: 0;
margin: -5px;
}
<h1>Header</h1>
<div class="box"></div>

The pseudo element (tomato color) is drawn behind the body background (#222) because it has a z-index < 0

If we remove the html tag background color:

body {
background: #222;
color: white;
box-sizing: border-box;
}
.box {
width: 200px;
height: 200px;
background-color: blue;
position: relative;
}
.box:before {
content: "";
background-color: tomato;
position: absolute;
z-index: -1;
inset: 0;
margin: -5px;
}
<h1>Header</h1>
<div class="box"></div>

Now the pseudo element (tomato color) is drawn after the body background (#222). That means that the body tag now creates a stacking context.

There are a few rules that determine if an element creates a stacking context. None of the rules explain why the body tag is now the root of a stacking context.

A similar question on SO explains, from the specs, that the canvas uses the body background color/image if the html tag has none. This is possibly to match the early days of HTML when this info was set with the bgcolor and background attributes of the body tag.

The question is: which part of the CSS specification is the reason of the body tag creating a stacking context depended on the html background color?


Solution

  • If we add a border to the body element, we can see that it is not in fact creating a stacking context. If the body element did create a stacking context, then the box would be painted over the border. But that doesn't happen: the border is painted over the box.

    body {
      background: #222;
      color: white;
      box-sizing: border-box;
      border:3px solid yellow;
    }
    .box {
      width: 200px;
      height: 200px;
      background-color: blue;
      position: relative;
    }
    .box:before {
      content: "";
      background-color: tomato;
    
      position: absolute;
      z-index: -1;
      inset: 0;
      margin: -5px;
    }
    <h1>Header</h1>
    <div class="box"></div>

    In contrast, if we force the body to create a stacking context - in the example below I've used scale: 1 to do that - we can see that now the box is indeed painted over the body's border.

    body {
      background: #222;
      color: white;
      box-sizing: border-box;
      border:3px solid yellow;
      scale: 1;
    }
    .box {
      width: 200px;
      height: 200px;
      background-color: blue;
      position: relative;
    }
    .box:before {
      content: "";
      background-color: tomato;
    
      position: absolute;
      z-index: -1;
      inset: 0;
      margin: -5px;
    }
    <h1>Header</h1>
    <div class="box"></div>

    So we can be sure that the propagation of the body background to the canvas is not causing the body to create a stacking context - it's merely having a similar outcome when the body has no border.