Understand the idea

Imagine two folders on a desk. Moving a sheet to the top of the lower folder does not put it above the upper folder. Nested stacking contexts behave like those groups.

A child’s z-index is compared within its stacking context. Positioned elements with a non-auto z-index can form these contexts; transforms and opacity below 1 are other common triggers. Inspect the ancestors as well as the overlapping element.

Read the example

Use two overlapping siblings inside .stage, with a .badge inside .panel.

CSS · EXAMPLE
.stage { position: relative; isolation: isolate; }
.panel { position: relative; z-index: 1; }
.panel .badge { position: absolute; z-index: 999; }
.neighbour { position: relative; z-index: 2; }
/* If the whole panel should be above its neighbour: */
.panel { z-index: 3; }

Make the comparison explicit

Write down the two elements that overlap. For each one, walk up the DOM until you find the contexts being compared as siblings. Those are the levels that matter.

Do not turn every element into a layer

Adding position and z-index everywhere makes later debugging harder. Keep layers deliberate and use a small documented scale where possible.

Practise: the badge behind a neighbouring panel

A small mistake, explained

What goes wrong

Increasing the badge to z-index: 999999 does not let it escape a panel that is behind its neighbour.

How to fix it. Adjust the relevant sibling stacking contexts, or move the overlay to a suitable place in the document. Check the intended layering before changing it.

Try it yourself

Overlap two panels. Give the lower panel a badge and raise only the badge’s z-index. Then raise its parent and compare.

Further reading

MDN — Why a huge z-index can still lose (new tab)

Original explanation and example prepared for HTML code FYI with AI assistance. Test the code in your own context. How these guides are made.