How z-index CSS property works?

How z-index CSS property works?

Not long ago I was assigned a bug where a floating notification was supposed to be displayed on top of any other element on the page but kept being overlayed by other elements. At first, I said to myself, "Easy!!! just increase the z-index value", to then find out that nothing changed, "ok, increase it even more!!!", guess what, no change. It seems that the seemingly simple z-index property is not that simple, I went on to discover what more there is, to now share with you.

What is the z-index property?

In a basic context, web pages can be considered two-dimensional, with every element aware of the space it and its neighbors take without overlapping.

But most modern applications are not that simple, and in cases where elements are overlapping z-index comes in handy, it lets you adjust the order of the layering of elements, where elements with a larger index are being displayed on top of those with smaller indexes.

It can be specified as, with the effects:

  • an <integer> value - this value is the order of the layer when rendering, and it also creates a stacking context(more on this later). Note: it accepts negative values

  • keyword auto - same as setting the value to 0, but without creating a staking context.

Let's go over some scenarios to strengthen our grasp on the subject.

Basic context without z-index

This example showcases the default stacking of elements if no z-index property is set. In this case, elements are stacked from top to bottom with non-positioned first, in order of appearance in HTML, then positioned elements in order of appearance in HTML.

In this example, Div1 is a non-positioned element, and even though it appears after Div0 in the HTML it is still displayed under it because Div0 is a positioned element.
Div0, Div2, Div3, and Div4 all being positioned elements are stacked in order of appearance in HTML.

The higher the z-index, the closer to the user

This one should be pretty straightforward, all elements have z-index property set, and an element with a higher z-index value is stacked on top of elements with a lower z-index value.
Pay attention to Div4 and Div5, since both have the same value set to the z-index property they are stacked based on the order of appearance in HTML.

Does not affect non-positioned elements

A thing worth mentioning is that the z-index property does not affect non-positioned elements (elements with position static). In this example even though Div1 has the z-index value set to 100 because it is not positioned it is stacked under the positioned elements(any position except static).

What is a stacking context?

Now, this is when things get a bit tricky and the cover image of this article starts to make sense in a way :)
How is possible for Div1 and Div4 to be stacked on top of Child1 or Child2 which have a higher z-index value? The answer is stacking context.
Let's take it step by step:

Setting the z-index property to anything other than auto creates a stacking context for that element.

The stacking context is treated atomically as a single unit in the parent stacking context.

Being a single unit means that each stacking context is self-contained, after the element's children are stacked(according to the same rules explained above), the whole element is stacked in the parent stacking context.

To stack the whole element in the parent stacking context the stacking context is completely independent of its siblings (children of two sibling elements can not interfere).

And since we have already mentioned parents, children, and siblings it can mean only one thing, stacking contexts can be contained in other stacking contexts, together creating a hierarchy of stacking contexts.

Looking at our example we see that all elements have z-index set, which means each element creates a stacking context.

Because Div2 has children, its children are stacked first (ignoring all the other elements outside Div2), Child1 has a higher z-index value than Child2, which means it will be on top of it, then because Child1 and Child2 are part of Div2's stacking context they will be positioned on top of Div2.

After Div2's contents have been stacked, the whole element is stacked relative to its siblings, Div2 having the lowest z-index value will have the lowest layer, then on top of it will be Div1 and then Div4, because Div1, Div2, and Div4 are part of document's root stacking context they will be positioned on top of the document's root element :)

It is worth mentioning that z-index is not the only property that creates a stacking context, to eliminate surprises check the official docs which describe every scenario that can create a stacking context.