CSS course menu
CSS z-index and Stacking Contexts: Why z-index Is Not Working
Understand how CSS decides which element appears on top: painting order, z-index on positioned elements and flex and grid items, stacking contexts created by opacity, transform and isolation, and how to debug z-index problems.
On this page
When elements overlap, something has to be drawn on top. z-index controls that order along an imaginary z-axis that points out of the screen towards you.
The default order#
Without z-index, positioned elements are painted after (on top of) non-positioned ones, and later elements in the HTML are painted on top of earlier ones:
.box { position: absolute; width: 110px; height: 80px; padding: 6px; color: white; font: 14px sans-serif; }
.a { background: #2f5fe0; top: 10px; left: 10px; }
.b { background: #e8590c; top: 40px; left: 70px; }
.c { background: #2f9e44; top: 70px; left: 130px; }<div class="box a">A (first)</div>
<div class="box b">B</div>
<div class="box c">C (last)</div>Using z-index#
z-index takes a whole number; higher numbers are drawn on top. It works on positioned elements (anything but static) and on flex and grid items:
.box { position: absolute; width: 110px; height: 80px; padding: 6px; color: white; font: 14px sans-serif; }
.a { background: #2f5fe0; top: 10px; left: 10px; z-index: 3; }
.b { background: #e8590c; top: 40px; left: 70px; z-index: 2; }
.c { background: #2f9e44; top: 70px; left: 130px; z-index: 1; }<div class="box a">A z-index: 3</div>
<div class="box b">B z-index: 2</div>
<div class="box c">C z-index: 1</div>Now A is on top. Negative values are allowed and put an element behind its siblings.
Stacking contexts: why z-index "does not work"#
A stacking context is a group whose children are layered inside it. The whole group is then layered as one unit with its siblings. A child can never escape its parent's stacking context, no matter how large its z-index is.
.panel { position: relative; width: 220px; height: 100px; padding: 8px; color: white; font: 14px sans-serif; }
.one { background: #2f5fe0; z-index: 1; }
.two { background: #e8590c; z-index: 2; margin-top: -60px; margin-left: 60px; }
.tooltip {
position: absolute;
z-index: 9999;
top: 50px;
left: 20px;
background: black;
padding: 6px;
}<div class="panel one">Panel one (z-index 1)
<div class="tooltip">Tooltip, z-index 9999</div>
</div>
<div class="panel two">Panel two (z-index 2)</div>The tooltip has z-index: 9999, yet panel two covers it. The tooltip lives inside panel one's stacking context, and panel one (1) is below panel two (2).
What creates a stacking context#
Many properties create one, including:
position: relativeorabsolutewith az-indexother thanauto;position: fixedorsticky;opacityless than 1;transform,filter,backdrop-filter,perspective,clip-pathormaskwith a value other thannone;- a flex or grid item with a
z-index; isolation: isolate;will-changenaming one of the properties above.
This is why adding opacity: 0.99 or a transform to a parent can suddenly change what appears on top.
isolation: isolate#
isolation: isolate creates a stacking context without any visual side effects. Use it on components so their internal z-index values cannot leak out and fight with the rest of the page:
.card { isolation: isolate; }
.card .decoration { position: absolute; z-index: -1; } /* stays behind the card's content, not the whole page */Debugging z-index#
- Check the element is positioned (or a flex/grid item); otherwise
z-indexis ignored. - Walk up its ancestors and look for anything that creates a stacking context.
- Compare the elements at the same level of stacking contexts, not the deeply nested ones.
Browsers' developer tools can help: Edge and Chrome have a 3D View / Layers panel that shows stacking visually.
Keep a small scale of values for a project, for example 1 for raised content, 10 for dropdowns, 100 for sticky headers and 1000 for modals, instead of ever-growing numbers.
Practice#
Practice exercise
Fix the example above so the tooltip appears above panel two, without changing the HTML.
Show solution
.panel { position: relative; width: 220px; height: 100px; padding: 8px; color: white; font: 14px sans-serif; }
.one { background: #2f5fe0; z-index: 3; }
.two { background: #e8590c; z-index: 2; margin-top: -60px; margin-left: 60px; }
.tooltip { position: absolute; z-index: 1; top: 50px; left: 20px; background: black; padding: 6px; }<div class="panel one">Panel one
<div class="tooltip">Tooltip, now on top</div>
</div>
<div class="panel two">Panel two</div>The tooltip can only rise as high as its parent's stacking context, so the fix is to raise panel one above panel two. (Another option is to remove z-index from both panels, so they no longer create stacking contexts.)
Key takeaway#
Finished this lesson?