Search code examples
cssz-index

Override CSS Z-Index Stacking Context


I'm trying to override / ignore the stacking context for an element so it can be positioned on the z-axis relative to the page root.

However, according to the article What No One Told You About Z-Index:

If an element is contained in a stacking context at the bottom of the stacking order, there is no way to get it to appear in front of another element in a different stacking context that is higher in the stacking order, even with a z-index of a billion!

New stacking contexts can be formed on an element in one of three ways:

  • When an element is the root element of a document (the element)
  • When an element has a position value other than static and a z-index value other than auto
  • When an element has an opacity value less than 1

With the following example:

.red, .green, .blue { position: absolute; }
.red   { background: red; }
.green { background: green; }
.blue  { background: blue; }
<div><span class="red">Red</span></div>
<div><span class="green">Green</span></div>
<div><span class="blue">Blue</span></div>

If the first div is given opacity:.99;, (which creates a new stacking context on the first node) then even if .red has z-index:1, it will still be placed behind the other elements because it is just rendered as the highest element within that stack.

Working Demo in jsFiddle

Which looks like this:

demo

Q: Is there a way for an element to ignore the stack context of any of it's parent elements and ask to be positioned relative to the original stack context of the page?


Solution

  • We can do it using 3D transformation and we will be able to bring any element to the front even if it's trapped inside a stacking context:

    .red,
    .green,
    .blue {
      position: absolute;
      width: 100px;
      color: white;
      line-height: 100px;
      text-align: center;
    }
    
    body,
    div:first-child {
      transform-style: preserve-3d; /* this is important for the trick to work */
    }
    .red {
      top: 20px;
      left: 20px;
      background: red;
      /*z-index: 1; we no more need this */
      transform:translateZ(1px); /* this will do the trick  */
    }
    
    .green {
      top: 60px;
      left: 60px;
      background: green;
    }
    
    .blue {
      top: 100px;
      left: 100px;
      background: blue;
    }
    <div><span class="red">Red</span></div>
    <div><span class="green">Green</span></div>
    <div><span class="blue">Blue</span></div>

    More details and examples here: Why can't an element with a z-index value cover its child?