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.
.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)
Keep exploring
- CSS · Guide
Find the source of horizontal overflowFix the oversized element instead of hiding the symptom.
- CSS · Guide
Selectors: choosing what to styleMatch the element you mean before adjusting its appearance.