Search code examples
htmlcsscss-transforms

How to control z-index when using perspective and transform?


I've read a lot of pages talking about z-index and how transform and perspective destroys his context. I understand why my code isn't working what I don't know is how to make it work (or at least use and alternative).

As you can see on the image, the select menu is being covered by the card below.

Image of the problem on the real page

Here is a snippet with a reproduction of the issue:

.square { position: relative; width: 100%; padding-bottom: 100%; }
.square>* { position: absolute; width: 100%; height: 100%; }

/* flip */
/* u -> uncontroled / c -> controled */
.flip { perspective: 200em; }
.flip .front, .flip .back {
	-webkit-backface-visibility: hidden; -moz-backface-visibility: hidden;
	backface-visibility: hidden; position: absolute;
	top: 0; left: 0; right: 0; bottom: 0;
}
@supports (-webkit-transform-style: preserve-3d) or (-moz-transform-style: preserve-3d) or (transform-style: preserve-3d) {
	.flip .front, .flip .back { 
		-webkit-transform-style: preserve-3d; -moz-transform-style: preserve-3d; transform-style: preserve-3d; transition: 1s;
	}
}
/* -> horizontal */
.flip .front { z-index: 2; transform: rotateY(0deg); perspective: none;}
.flip .back { transform: rotateY(-180deg); }
.flip.u:hover .back, .flip.c.active .back { transform: rotateY(0deg); }
.flip.u:hover .front, .flip.c.active .front { transform: rotateY(180deg); }
/* -> vertical */
.flip.vertical .back { transform: rotateX(-180deg); }
.flip.vertical.u:hover .back, .flip.vertical.c.active .back { transform: rotateX(0deg); }
.flip.vertical.u:hover .front, .flip.vertical.c.active .front { transform: rotateX(180deg);  }

.test {
	position: absolute; top: 70%; left: 10%; background-color: green;
	height: 100px; width: 10px; z-index: 100;
}
<div style="width: 100px; background-color: black">
  <div class="square flip u">
    <div class="front" style="background-color: blue">
      Front
      <div class="test"></div>
    </div>
    <div class="back" style="background-color: red">Back</div>
  </div>
  <hr >
  <div class="square flip u">
    <div class="front" style="background-color: blue">Front</div>
    <div class="back" style="background-color: red">Back</div>
  </div>
</div>

What I need is the <div class="test"> to be displayed in front of all. Like this. But I don't want to set the z-index on the square divs, because they will be placed inside a flex layout Grid.

Is there a way to say to an element to be always rendered in front of all other? No mathers where is he placed or what is happening to the stack context.


Solution

  • Nobody has found a solution over this last years so here you have what I finally did (as an alternative to solve my specific problem):

    I just changed the z-index of all child elements, using js, making thos childs that are first having a higher z-index.

    var targets = document.getElementsByClassName("invertChildZOrder");
    
    var i;
    for (i = 0; i < targets.length; i++) {
      target = targets[i];
      for (var i = 0; i < target.childNodes.length; i++) {
        var childNode = target.childNodes[i];
        if (childNode.className == "square flip u") {
          childNode.style.zIndex = "" + (target.childNodes.length - i);
        }        
      }
    }
    .square { position: relative; width: 100%; padding-bottom: 100%; }
    .square>* { position: absolute; width: 100%; height: 100%; }
    
    /* flip */
    /* u -> uncontroled / c -> controled */
    .flip { perspective: 200em; }
    .flip .front, .flip .back {
      -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden;
      backface-visibility: hidden; position: absolute;
      top: 0; left: 0; right: 0; bottom: 0;
    }
    @supports (-webkit-transform-style: preserve-3d) or (-moz-transform-style: preserve-3d) or (transform-style: preserve-3d) {
      .flip .front, .flip .back { 
        -webkit-transform-style: preserve-3d; -moz-transform-style: preserve-3d; transform-style: preserve-3d; transition: 1s;
      }
    }
    /* -> horizontal */
    .flip .front { z-index: 2; transform: rotateY(0deg); perspective: none;}
    .flip .back { transform: rotateY(-180deg); }
    .flip.u:hover .back, .flip.c.active .back { transform: rotateY(0deg); }
    .flip.u:hover .front, .flip.c.active .front { transform: rotateY(180deg); }
    /* -> vertical */
    .flip.vertical .back { transform: rotateX(-180deg); }
    .flip.vertical.u:hover .back, .flip.vertical.c.active .back { transform: rotateX(0deg); }
    .flip.vertical.u:hover .front, .flip.vertical.c.active .front { transform: rotateX(180deg);  }
    
    .test {
      position: absolute; top: 70%; left: 10%; background-color: green;
      height: 100px; width: 10px; z-index: 100;
    }
     <div class="invertChildZOrder" style="width: 100px; background-color: black">
      <div class="square flip u">
        <div class="front" style="background-color: blue">
          Front
          <div class="test"></div>
        </div>
        <div class="back" style="background-color: red">Back</div>
      </div>
      <hr >
      <div class="square flip u">
        <div class="front" style="background-color: blue">Front</div>
        <div class="back" style="background-color: red">Back</div>
      </div>
    </div>

    As you can imagine. This only solves the problem partially. If the square on the botton has another menu but this times droping up instead of down we will have same problem.