CSS3 transitions and z-index

You can apply CSS3 transitions to the z-index property, but it may work in a way you don’t expect.

This is just a reminder that you can apply transitions to an element’s z-index (aka, where it lays in the stack), but only by stepping through the layers. The browser has to keep z-index in whole numbers. This makes total sense, but it’s something I forgot about on a recent project.

I was working on an interface with overlapping elements, and I wanted whichever one was hovered to come to the front of the stack. I wanted this move to the front to look smoother, instead of one element just jumping out from behind another one suddenly. “Aha!” my brain said. “I will add a transition!” But my brain hadn’t thought this all the way through, and, of course, it didn’t work.

If you stop to think about it (which I eventually did), this makes perfect sense. If you want to take one element with z-index:1 and transition it to lay on top of another element with z-index:2, how in the world is the browser supposed to create intermediate steps between those two values? You can’t have an element with a z-index value of 1.5. An element can’t kinda overlap another element. Either it’s above or below. The browser can’t create some weird, merged together state where the two elements are on the same level and intermixing, both partially transparent on just the spots that are sharing the same level.*

What the browser can do is increment the z-index value in whole numbers over the course of your transition duration. So, if I have an element with a z-index value of 1 and I want to gradually get to 10, the browser can first set z-index to 2, then 3, then 4, and so on. If you have a short transition duration or a long way to go from your starting value to your ending value (like changing z-index from 1 to 1000), it will step through these changes very quickly.

Here’s a demo of z-index transitioning slowly so you can see how the browser increases the z-index value step by step; hover over the block labeled One to see it happen. Here are some screenshots of the same demo taken along the course of the transition.

The first div is naturally at the bottom of the stack.

The first div is naturally at the bottom of the stack (z-index: 1).

When hovered and the transition begins, it first jumps on top of the Two div.

When hovered and the transition begins, it first jumps completely on top of the Two div as it simultaneously becomes opaque.

Notice that it is never partly on top and partly below any given div. It's entirely above divs Two and Three here, and entirely below divs Four and Five.

Notice that it is never partly on top and partly below any given div. It’s entirely above divs Two and Three here, and entirely below divs Four and Five.

The transition is almost over now. It's worked it's way up to z-index 4 at this point.

The transition is almost over now. It’s worked it’s way up to z-index: 4 at this point.

By the end of the transition, it has z-index of 6 and is on top of all the other divs.

By the end of the transition, it has z-index: 6 and is on top of all the other divs.

So remember: z-index is indeed one of the properties you can apply a transition to, but the transition has to happen in full steps, not necessarily as gradually as you might imagine it in your head.

* If the elements you’re transitioning z-index on are partially transparent (using HSLA for the background color, for instance) to begin with, you can kind of fake this effect. But if you look closely, you can still see the moment the element swaps places with another in the stack. It’s just harder to notice.

Did you like this?

14 Responses to “CSS3 transitions and z-index”

  1. Schepp

    To achieve more of a smooth float upwards you might tinker around with CSS 3D transforms, setting a 3D perspective and then transitioning translateZ. Although the overlap will switch as suddenly as it does with z-index, the advantage is that the element also grows in relation to the others which tricks you into seeing a smooth transition.

    Reply
    • Zoe Gillenwater

      Schepp, yes, good idea for another way to disguise it, if you wanted the item to grow as well as “move forward.” In my case, my item wasn’t changing size, just re-layering and changing opacity, so there were not many other CSS effects to hide behind. This makes me think of a magician distracting you with his right hand while he performs the real trick with his left hand. 🙂

      Reply
  2. m_gol

    It has nothing to do with z-index having to be integer. If 1.5 was allowed there still would be no graduation as 1.5 is strictly greater than 1.

    Reply
  3. John

    Cool, I would have thought that since it was integers only you wouldn’t be able to apply a transition to.

    Reply
  4. Yeelan

    Thank you so much for this article.

    I have been stucked by the “pop out” question for a while and had never really thought it was caused by my unproper z-index value.

    Reply
  5. Duke3D

    The appearance of gradually moving higher in z-order can be simulated. Assume the non-moving object is at z-level 2. Create two identical instances of the moving object, one at Z-level 1 at full opacity and the other at z-level 3 initially at zero opacity and animate the opacity. The object will appear to smoothly pass through the other. As suggested, you can also add a scale transform equally to both instances to grow the object as it moves forward.

    Reply
    • mark

      I was wondering if you had a simple working example of this unique and clever solution.
      That would be so helpful
      thanks.

      Reply
  6. Mark

    Thanks for helping me think through this! I had a similar issue recently.

    Reading this, I realized that I could use transition on my z-index to delay when when it changed so that the order was correct. So my [incomplete] code worked like this:


    a {
    transition: transform transform 0.3s linear 0.1s, z-index 1s linear 0s; // when returning from hover, transition scale and delay z-index returning to 0 until after scale transition is complete
    }
    a:hover {
    transform: scale(1.1,1.1);
    z-index: 1;
    transition: transform transform 0.3s linear 0.1s; // when hovered, immediately change z-index to top, transition scale
    }

    Reply

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>