Z-Index is one of the most confusing and unintuitive properties in CSS, but it’s actually pretty simple once you understand it.

Your first instinct is probably to think that you can just set z-index on any element and that that alone is going to determine its stacking order. This is WRONG.

First of all: z-index only applies to positioned elements.

A positioned element is an element who’s position property is NOT static (eg. relative , absolute , fixed ). Setting a z-index on an unpositioned element does nothing.

If a positioned element has a z-index of X, then all of its children will also be stuck with a z-value of X

You can change the z-index of any child elements all you want, but that won’t do anything!

(Note: z-value is not an actual CSS term. I only use it because z-index technically defaults to “auto” and can be set to whatever you want, even if it does nothing)

<div id="parent" style="position: relative; z-index: 6;">

<div id="child" style="position: relative: z-index: 7;">

<div id="grandchild" style="position: relative; z-index: 9;" >

</div>

</div>

</div>

The z-value of each of these elements is 6, and there’s no way to change that without modifying the z-index of #parent.

In technical terms, a stacking context is formed when a positioned element has a z-index. A stacking context is a single atomic unit composed of a parent along with its children. All elements within a stacking context are bound to the z-value set by the non-root parent (in this case #parent).

(Non-root just means that it’s not the <html> tag. The root <html> tag technically forms a stacking context, but any z-index on it is meaningless.)

So you can create stacking contexts within stacking contexts, but all child stacking contexts will be bound to the z-value of the outermost (non-root) stacking context.

In the example above, three stacking contexts are formed, and they’re nested. The z-value of all three contexts is bound to the z-index of #parent, which is 6.