115

Using transform property, z-index is canceled and appeared in the front. (When commenting out -webkit-transform, z-index is properly working in below code)

.test {
  width: 150px;
  height: 40px;
  margin: 30px;
  line-height: 40px;
  position: relative;
  background: white;
  -webkit-transform: rotate(10deg);
}

.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;
}
<html>

<head>
  <title>transform</title>
  <link rel="stylesheet" type="text/css" href="transformtest.css">
</head>

<body>
  <div class="test">z-index is canceled.</div>
</body>

</html>

How do transform and z-index work together?

Lionel
  • 90
  • 1
  • 9
kbth
  • 1,155
  • 2
  • 8
  • 7

7 Answers7

176

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>

There are other ways to do this, better ways even. I would probably make the "post-it" background the containing element and then put the text inside, that would probably be the easiest method and would reduce the complexity of what you have.

Check out this article for more details about z-index and stacking order, or the working W3C CSS3 spec on stacking context

TylerH
  • 20,799
  • 66
  • 75
  • 101
Lochlan
  • 2,666
  • 3
  • 23
  • 25
  • 8
    This was a great explanation of one of the most complex aspects of CSS layout. For future reading, [check the W3 spec](http://www.w3.org/TR/CSS2/zindex.html) – AndyBean Apr 21 '14 at 16:11
  • Fantastic explanation. I was using `transform` on my parent element and was wondering why my `::before` element was not stacked behind the parent. – OGreeni Nov 08 '22 at 18:59
  • I am using it in the pseudo-element, and I can't make it keeps its `z-index` value. – SalahAdDin Nov 21 '22 at 11:10
28

Set the div you want to stay on top to position:relative

AGrush
  • 1,107
  • 13
  • 32
  • 2
    It worked for me. Is it related with the stacks context mentioned above? If someone can help to clarify it will be greatly appreciated. – glihm Jul 28 '19 at 06:52
  • maybe it's to do with when you have a position of relative (or absolute) and a z-index set, a new stacking context is created? – nCardot May 26 '21 at 02:06
  • Thanks for this! Worked like a charm after trying to set CSS for elements that are not selected of the same class!! – zqlimy Aug 09 '21 at 07:23
11

Had a similar problem where siblings were being transform: translate()'d and z-index wouldn't work.

Most straightforward solution is to set position: relative on all siblings, then z-index would work again.

seeker_of_bacon
  • 1,939
  • 2
  • 19
  • 20
  • I suppose this would create a new stacking context for each sibling and therefore the `z-index` would only work within the parent but can not overlap the parent's siblings, correct? At least that's what I'm struggling to achieve. – tgikf May 12 '23 at 07:08
6

Quick fix: You could just rotate the other element by 0 degrees as well.

Guy
  • 1,254
  • 17
  • 16
  • This worked for me in a case where I could not rotate the container – Daniel Flippance Jan 28 '19 at 18:05
  • 1
    I was having a strange problem with z-index not being observed, and then applying the style `transform:rotate(0.0rad)` fixed it. I could not apply it via a css rule, it had to be directly on the element. This makes NO sense, but thanks for posting the answer that made me try it. – Andrew Shepherd Jan 30 '19 at 04:12
5

For those who still looking for the solution, I found this article how to solve issue with transform and z-index here

Simple usage of it is by doing this:

.parent { transform-style: preserve-3d; }
.parent:before { transform: translateZ(-1em); }
GYTO
  • 440
  • 1
  • 10
  • 23
1

I was facing the similar problem. What i did was, I added a wrapper div around the test and gave the transform property to the wrapper div.

.wrapper{
    transform: rotate(10deg);
}

here is the fiddle http://jsfiddle.net/KmnF2/16/

Rambatino
  • 4,716
  • 1
  • 33
  • 56
Ankur Sahu
  • 134
  • 2
  • 12
-4

Set the div you want to stay on top to position:absolute