When you use the :before and :after pseudo-elements to display content behind their parent element, you often find that this content gets displayed on top of the parent, even though you’ve set z-index:-1. And when it works, it may fail when you nest instances of the parent element. Here’s why.

It is important to realize that pseudo-elements are considered descendants of their associated element. You may set a negative z-index for these pseudo-elements, but in order for them to actually appear below their parent element, you must create a new stacking context for the parent.

This, for instance, does not work:

.parent {
  position: relative;
  width: 200px;
  height: 200px;
  background: pink;
  z-index: 0;
}

.parent:before {
  position: absolute;
  width: 200px;
  height: 200px;
  left: 10px;
  top: 10px;
  z-index: -1;
}

The above code can be made to work by removing the z-index from the parent element. However, this will still fail when you nest multiple elements, because then the stacking context is no longer correct.

The trick to making this work is by creating a stacking context on a container element around the parent element. This container elements must have a position, as well as a z-index.

.container {
  position: relative;
  z-index: 1;
}

.parent {
  position: relative;
  width: 200px;
  height: 200px;
  background: pink;
  /* No z-index allowed! */
}

.parent:before {
  position: absolute;
  width: 200px;
  height: 200px;
  left: 10px;
  top: 10px;
  z-index: -1;
}

Therefore: you must create a new stacking context on the parent’s parent (the container) by giving it a position and a z-index. The parent itself must not have a z-index.