Stacking and z-index
Prerequisites: Positioning
When elements overlap, the stacking order decides which is on top.
- By default, later elements in the HTML paint over earlier ones, and positioned elements paint over non-positioned ones.
z-indexchanges the order of positioned elements (and flex and grid items). Higher numbers are closer to you.
Stacking contexts
The surprise: z-index only compares elements within the same
stacking context. Some properties create a new context, and
everything inside it is stacked as one unit relative to the outside:
positionother thanstaticwith az-indexother thanautoposition: fixedorstickyopacityless than 1transform,filter,backdrop-filter,perspectiveisolation: isolatewill-changenaming one of those properties- flex and grid items with a
z-index
<style>
.a { position: relative; z-index: 1; }
.a .tooltip { position: absolute; z-index: 9999; }
.b { position: relative; z-index: 2; }
</style>
<div class="a"><div class="tooltip">Tooltip</div></div>
<div class="b">Header</div>
<script>
const tip = document.querySelector(".tooltip");
const b = document.querySelector(".b");
tip.style.top = "0";
const r = b.getBoundingClientRect();
tip.style.top = (r.top - tip.parentElement.getBoundingClientRect().top) + "px";
const topEl = document.elementFromPoint(r.left + 5, r.top + 5);
console.log("on top:", topEl.className);
</script>
on top: b
The tooltip has z-index: 9999 but still sits under .b: it lives
inside .a's stacking context, and .a (1) is below .b (2). No
z-index inside .a can escape that.
Practical advice
- Keep z-index values in a small, documented scale (for example, custom
properties
--z-dropdown: 10,--z-sticky: 20,--z-modal: 100) — not escalating 9999s. - Use
isolation: isolateon a component to contain its internal z-indexes without changing its own position. - When z-index "doesn't work", look for an ancestor that creates a stacking context.
- The top layer — used by
dialogwithshowModal(), popovers, and full screen — sits above every stacking context, which is why these features solve most overlay problems without z-index.