1

UPDATE: This question was marked as duplicate by a friend, but I think the answer is still very valuable. I looked into those answers in duplicate questions and no one mentioned transform-style: preserve-3d can do transform without creating new stacking context. So this problem is more specific than how z-index works. It's also about how transform works.


I was trying to add some animation when hovering over a div element. But when I added transform in hover, its pseudo child element's background color covered div's. It seems that this wired behavior only happens when using transform. I want to know what's the mechanism behind this behavior.

In the following codepen example, the first one is hover with transform, the second one is normal hover.

https://codepen.io/neirongkuifa/pen/PgaEZd

.container {
 width: 100px;
 height: 100px;
 background-color: red;
 position: relative;
  margin-bottom:100px;
}

.move:hover {
 transform: translateY(3px);
}

.changeColor:hover{
  background-color:white
}

.container::after {
 content: '';
 display: inline-block;
 position: absolute;
 top: 0;
 left: 0;
 background-color: green;
 width: 150px;
 height: 150px;
 z-index: -1;
}
<div class="container move">Content</div>
<div class="container changeColor">Content</div>
ch48h2o
  • 155
  • 1
  • 7
  • Before hovering, container and its pseudo after element are actually siblings under root's stacking context. pseudo after element has z-index -1, so it's rendered first and than container. That's why container is on top of pseudo after element. However when hovering over container with transform, container creates its own stacking context. In this context, container and its pseudo after element are not siblings any more. They are parent and child and parent always renders first. z-index is meaningful when compared with its siblings. pseudo after element has no siblings hence meaningless here. – ch48h2o Apr 21 '19 at 12:28

1 Answers1

2

You are creating a new stack context, and the z-index behaves differently.

Your best solution is to handle everything using transforms. I have added a transformZ negative in the pseudo to move it backwards, and a preserve-ed on the item to make this work:

.container {
    width: 100px;
    height: 100px;
    background-color: red;
    position: relative;
  margin-bottom:100px;
  transform-style: preserve-3D;   /*added*/
}

.move:hover {
    transform: translateY(3px);
}

.changeColor:hover{
  background-color:white
}

.container::after {
    content: '';
    display: inline-block;
    position: absolute;
    top: 0;
    left: 0;
    background-color: green;
    width: 150px;
    height: 150px;
    z-index: -1;
    transform: translateZ(-1px); /*added*/
}
<div class="container move">Content</div>
<div class="container changeColor">Content</div>
vals
  • 61,425
  • 11
  • 89
  • 138