Css Z-Index Lost After Webkit Transform Translate3D

css z-index lost after webkit transform translate3d

This might be related to: https://bugs.webkit.org/show_bug.cgi?id=61824

Basically when you apply a 3D transform on the z-axis, the z-index can't be accounted for anymore (you're now in a 3 dimensional rendering plane, use different z-values). If you want to switch back to 2D rendering for child elements, use transform-style: flat;.

z-index is canceled by setting transform(rotate)

Let's walk through what is occurring. To start, note that z-index on positioned elements and transform by itself create new "stacking contexts" on elements. Here's what's going on:

Your .test element has transform set to something other than none, which gives it its own stacking context.

You then add a .test:after pseudo-element, which is a child of .test. This child has z-index: -1, setting the stack level of .test:after within the stacking context of .test Setting z-index: -1 on .test:after does not place it behind .test because z-index only has meaning within a given stacking context.

When you remove -webkit-transform from .test it removes its stacking context, causing .test and .test:after to share a stacking context (that of <html>) and making .test:after go behind .test. Note that after removing .test's -webkit-transform rule you can, once again, give it its own stacking context by setting a new z-index rule (any value) on .test (again, because it is positioned)!

So how do we solve your problem?

To get z-index working the way you expect, make sure that .test and .test:after share the same stacking context. The problem is that you want .test rotated with transform, but to do so means creating its own stacking context. Fortunately, placing .test in a wrapping container and rotating that will still allow its children to share a stacking context while also rotating both.

  • Here's what you started with: http://jsfiddle.net/fH64Q/

  • And here's a way you can get around the stacking-contexts and keep
    the rotation (note that the shadow gets a bit cut off because of .test's white background):

.wrapper {    -webkit-transform: rotate(10deg);}.test {       width: 150px;       height: 40px;       margin: 30px;       line-height: 40px;       position: relative;       background: white;}.test:after {       width: 100px;       height: 35px;       content: "";       position: absolute;       top: 0;       right: 2px;       -webkit-box-shadow: 0 5px 5px #999; /* Safari and Chrome */       -webkit-transform: rotate(3deg); /* Safari and Chrome */       transform: rotate(3deg);       z-index: -1;}
<div class="wrapper">    <div class="test">z-index is canceled.</div></div>

z-index lost until end of transition

This behavior is logical, it's a bit difficult to explain but it's due to the direction of the rotation. If you look closely you will see that the right part turn by going to the top (considering the Z axis) but the the left part turn by going to the bottom that why it's hidden while rotating.

In order to correct this, you need to inverse the rotation of the left part to make it behave like the right one. Instead of 0deg you need to use 360deg.

var button = $('.button');var open = true;
button.click(() => { var right = $('.triangle.topright'); var left = $('.triangle.topleft');
if (open) { right.addClass('r-close'); left.addClass('l-close'); } else { right.removeClass('r-close'); left.removeClass('l-close'); }
open = !open;});
.container {  box-sizing: border-box;  display: flex;  padding: 50px;  position: relative;  width: 100%;}
.fold.r-close { transform-origin: left; transform: rotate3d(0, 1, 0, 90deg);}
.fold { box-sizing: border-box; display: inline-block; height: 100px; width: 100px; position: relative; transition: all 1.5s; transform-style: preserve-3d;}
.fold.outer { width: 150px;}
.fold.left { margin-left: 50px;}
.triangle { height: 0; position: absolute; transition: all 1.5s; width: 0;}
/* middle */.triangle.middle { top: 0; z-index: -1 !important;}
.triangle.middle.left { border-bottom: 100px solid pink; border-left: 100px solid transparent; right: 0;}
.triangle.middle.right { border-bottom: 100px solid pink; border-right: 100px solid transparent; left: 0;}
/* right fold */.triangle.topright.front { border-bottom: 100px solid red; border-right: 100px solid transparent; backface-visibility: hidden; transform: rotate3d(1, 1, 0, 180deg);}
.triangle.topright.back { backface-visibility: hidden; border-left: 100px solid transparent; border-top: 100px solid pink; left: -0.6px;}
/* left fold */.triangle.topleft.front { border-bottom: 100px solid red; border-left: 100px solid transparent; backface-visibility: hidden; transform: rotate3d(-1, 1, 0, 180deg);}
.triangle.topleft.back { backface-visibility: hidden; border-top: 100px solid pink; border-right: 100px solid transparent; right: -0.6px;}
/* folds */.r-close.back { transform: rotate3d(1, 1, 0, 180deg);}
.triangle.r-close.front { transform: rotate3d(1, 1, 0, 0deg);}
.l-close.back { transform: rotate3d(-1, 1, 0, 180deg);}
.triangle.l-close.front { transform: rotate3d(-1, 1, 0, 360deg); /*Updated this*/}
.button { bottom: 0; position: absolute;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class="container">  <div class="fold outer">    <div class="triangle middle left"></div>    <div class="fold left">      <div class="triangle topleft front"></div>      <div class="triangle topleft back"></div>    </div>  </div>  <div class="fold outer">    <div class="triangle middle right"></div>    <div class="fold right">      <div class="triangle topright front"></div>      <div class="triangle topright back"></div>    </div>  </div>  <button class="button">Click</button></div>

transform:scale() breaking my z-index order

This is a known issue:

  • css z-index lost after webkit transform translate3d
  • webkit-transform breaks z-index on Safari
  • webkit-transform overwrites z-index ordering in Chrome 13

You can read more about it here as it offers in depth explanation.

In addition to opacity, several newer CSS properties also create stacking contexts. These include: transforms, filters, css-regions, paged media, and possibly others. As a general rule, it seems that if a CSS property requires rendering in an offscreen context, it must create a new stacking context.

You could avoid this issue by moving the transform properties from #blocks-both to #block-main and #block-sidebar like this:

#block-main, #block-sidebar {
transform: scale(0.9);
}

#block-main {
transform-origin: 100% 0;
}

#block-sidebar {
transform-origin: 0 0;
}

I've also edited your example.

webkit-transform overwrites z-index ordering in Chrome 13

Solved it myself through trial and error. Thought I'd report back if someone else stumbles upon this problem. It shall still be noted that this problem did not occur before Chrome updated itself to Chrome 13 (13.0.782.107m).

The trick here seems to be to add a translate3d operation to the underlying <div> (sq2) element upon declaration (or atleast before animating sq1).

Otherwise, the translate3d operation on the overlying <div> (sq1) will cause rendering to ignore the z-index and place sq1 below sq2. I'm guessing that this is because sq1 is defined before sq2 in the DOM, therefore sq2 will be rendered above it.

So, the solution seems to be to put translate3d in the definition of the <div>:s (add it to both just to be clear):

HTML:
<div id="sq1" style="z-index:10; -webkit-transform: translate3d(0px, 0px, 0px);">
<div id="sq2" style="z-index:5; -webkit-transform: translate3d(0px, 0px, 0px);">

Layering using z-index with translate3d

This has nothing to do with transforms - just add a z-index of greater than your header to #wrapper

But I feel like I'm missing something from your question...

header{  position: fixed;  top: 0;  left: 0;  right: 0;  height: 40px;  background: #000;  color: #fff;  z-index: 100;}
#wrapper{ position: relative; z-index:101;}
.wrapper-scroll{ transform: translate3d(0,0,0); position: relative; z-index: 101;}
.child-above{ position: absolute; top: 0; left: 0; display: block; width: 200px; height: 200px; background: #555; z-index: 9999; padding: 16px 10px; color: red;}
<header>HEADER</header><div id="wrapper">  <div class="wrapper-scroll">    <div class="child-above">CHILD TO BE ABOVE HEADER</div>    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Facilis recusandae, tenetur assumenda sequi quos voluptates voluptatum voluptas. Officia quisquam eveniet totam reprehenderit vel neque est dolorem assumenda in voluptatem. Ab accusantium provident, ut, obcaecati sapiente vitae officia. Distinctio assumenda ex deserunt iusto tempore optio, mollitia amet quas! Aut, magni, repellendus.</p>  </div></div>

CSS3 transform: translate3d doesn't affect the z-axis?

Got it. Forgot to add -webkit-transform-style: preserve-3d;

CSS: Absolutely positioned pseudo element loses z-index?

I found this on w3.org, which I think explains it: http://www.w3.org/TR/css3-transforms/#effects

Any value other than ‘none’ for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.

As I understand it, your li:after pseudo-element is inside the stacking context of the li.active element, and therefore cannot appear behind it.



Related Topics



Leave a reply



Submit