Layer Order Without Magic Numbers
Z-Index & Stacking
Manage stacking with z-index utilities and understand when a stacking context is created.
What you'll learn
- Use the z-index scale and arbitrary values
- Know what creates a stacking context
- Use isolate to contain z-index
z-index decides what sits on top of what. Tailwind gives you a small scale so you stop inventing z-index: 9999 numbers that fight each other forever.
The z-index Scale
<header class="sticky top-0 z-10">nav</header>
<div class="z-50">modal layer</div>
<div class="z-[999]">rare escape hatch</div> The scale goes z-0, z-10, z-20, z-30, z-40, z-50, plus z-auto. Standardizing on these prevents the arms race. Arbitrary values like z-[999] exist but should be a red flag prompting you to rethink the layering.
z-index Only Works on Participants
A crucial gotcha: z-index has no effect on a static-positioned element. It only applies to elements that are positioned (relative, absolute, fixed, sticky) or are flex/grid children. If your z-50 does nothing, the element is almost always still static:
<!-- z-10 is ignored: still static -->
<div class="z-10">no effect</div>
<!-- z-10 now works -->
<div class="relative z-10">on top</div> Contain Layers with isolate
z-index values compete within the same stacking context. A common bug is a card’s decorative layer at z-10 accidentally rendering above an unrelated dropdown elsewhere on the page. isolate creates a fresh stacking context, sandboxing all child z-index values inside the component:
<div class="relative isolate">
<div class="absolute inset-0 -z-10 bg-gradient-to-br from-blue-100"></div>
<div class="relative z-10">content above the decorative gradient</div>
</div> Because of isolate, the inner -z-10 and z-10 only matter relative to each other — they can never escape the card and clash with the rest of the page. Reaching for isolate is almost always better than escalating z-index numbers.