Z-Indexes. They seem simple enough. At least they seem to simple enough that Microsoft could implement them without messing it up. Unfortunately, this is not the case.

The z-index bug in Internet Explorer is troublesome because it affects both IE6 and IE7. Unlike many of the Internet Explorer bugs, this one is likely to stick around for a while.

So how did Microsoft screw up this one? Imagine we have the simple markup shown here:

<div id="Box1">
    Box 1 (z-index: 10)
    <div id="Box2">
        Box 2 (z-index: 20)
    </div>
</div>

<div id="Box3">
    Box 3 (z-index: 15)
</div>

As you can see, we have three boxes, with the second box inside the first one. Now lets play with the positioning and z-indexes of these boxes:

#box1
{
    background: #ccc;
    z-index: 10;

    width: 330px;
    height: 200px;
    position: absolute;
    top: 75px;
    left: 5px;
}

#box2
{
    z-index: 20;

    width: 210px;
    height: 100px;
    position: absolute;
    top: 50px;
    left: 80px;
}

#box3
{
    z-index: 15;

    width: 300px;
    height: 260px;
    position: absolute;
    left: 270px;
    top: 35px;
}

div { color: #000; padding: 5px; border: solid 3px #000; }

All we’ve done here is played with the z-indexes and positioning a bit. The important thing that we’ve done is that all three boxes have absolute positioning and we’ve placed the third box on top of the other two boxes. The result looks like the following:

Positioning of elements in example

The question is, where does Box 3 go? Does it go on top of both boxes, behind both, or between the two? Lets add some colors to see:

...
#box2
{
    z-index: 20;
    background: #0cc;
    ...
}
#box3
{
    z-index: 15;
    background: #cc0;
    ...
}

The result is shown below:

Screenshot showing ordering

Surprised? It may seem strange to see a box with a z-index of 15 showing up on top of a box with a z-index of 20, but this is correct behavior.

The reason this happens is because a concept in CSS called a stacking context. The z-index property only applies to other elements that are in the same stacking context. When you specify a z-index for a positioned element, you begin a new stacking context within that element.

So in the example above, Box 1 and Box 3 are in the same stacking context, thus Box 3 goes on top of Box 1. Box 2 however is within the stacking context of Box 1, and thus is considered to be at the same z-index as its parent when comparing it to Box 3.

Ok, so where is the problem?

The problem comes when you set the z-index of Box 1 to “auto”:

#box1
{
    ...
    z-index: auto;
    ...
}

What should happen when we encounter this scenario? According to the CSS2 specification:

Positioned elements with ‘z-index: auto’ (in layer 6), floats (layer 4), inline blocks (layer 5), and inline tables (layer 5), are painted as if those elements generated new stacking contexts, except that their positioned descendants and any child stacking contexts take part in the current stacking context.

What that means is that Box 1 should not begin a new stacking context and Box 2 should be considered in the same stacking context as Box 3. Because Box 2 has a higher z-index than Box 3, it should be on top. When we view this new scenario in Firefox, we get the correct result as shown:

Image of the correct behavior

Now what happens when we view this page in Internet Explorer? We get the following result:

Image of Internet Explorer's behavior

Hmmm…so where did Internet Explorer go wrong?

The problem is that when Internet Explorer encounters a positioned element with a z-index of “auto”, it does start a new stacking context. This causes the same scenario as we had before, and Box 3 shows up on top of Box 2.

So what can you do to get around this? Regretfully, there’s not much that can be done as an immediate work around.

If all you’re trying to do is get Box 2 on top of Box 3 (and you don’t care where Box 1 is), then simply give Box 1 a z-index that is higher than Box 3. This may seem like a silly suggestion, but if box 1 is transparent, then knowledge of this trick becomes far more useful.

If you’re trying to get Box 3 to show up between Boxes 1 and 2, then there is not much that can be done. The only real solution is to design your markup in such a way where you can avoid this bug all together. This bug is relatively constrained in is scope, so it is usually avoidable.

No matter what you end up doing, it is good to know the details of the bug. If you understand the problem, then you will have an easier time fixing the problem when you encounter it.