| |

Tailwind CSS 19 🎨 Z-Index Layering and Stacking Context Management

The z-index property controls which element appears on top when two elements overlap. It is the last line of defense in CSS layout: when positioning, transforms, or opacity cause elements to overlap, z-index decides the order. But z-index is also one of the most misunderstood properties in CSS, because it does not operate globally. It operates within a stacking context, and an element’s z-index only competes with the elements in the same context. An element with z-index: 9999 can still appear below an element with z-index: 1 if the two are in different stacking contexts, and one context is stacked above the other.

This chapter covers the z-index utilities, the creation of stacking contexts, the rules that determine which context is on top, and the patterns that prevent the “z-index does not work” problem. The goal is to understand stacking as a hierarchy of contexts rather than as a single global ordering.

Key point: A z-index value only competes within its own stacking context. A stacking context is created by positioned elements with a z-index other than auto, by transform, opacity, filter, isolation, and several other properties. To fix a z-index problem, find the stacking context that is on top and adjust that context’s position, not the z-index inside a lower context.


Why z-index needs a mental model

The flat-model assumption problem. Developers often assume that z-index is a single global ordering: a higher number is always on top. This is true only when all the elements are in the same stacking context. When stacking contexts nest, the ordering is hierarchical, and a higher number inside a lower context does not escape that context.

The accidental context problem. Stacking contexts are created by properties that do not obviously relate to stacking: transform, opacity less than 1, filter, isolation: isolate, mix-blend-mode, and will-change. A component that applies transform: translateZ(0) for a hardware-acceleration hint creates a stacking context, and any z-index inside that component is now relative to the component’s siblings, not to the whole page.

The debugging problem. When a dropdown appears below a modal, or a tooltip is hidden behind a header, the cause is usually a stacking context, not a wrong z-index value. Finding the context that is on top is the first step, and adjusting the z-index of that context (or the position of the element that creates it) is the fix.

The layering problem. Applications have a natural layering: page content, sticky elements, dropdowns, modals, toasts. Each layer should be above the previous one. Encoding this hierarchy in a small set of named z-index values prevents the ad-hoc escalation that produces z-index: 9999 everywhere.

The interaction problem. Tailwind provides z-0 through z-50 in steps of 10, plus arbitrary values. The utilities are the values; the understanding of stacking contexts is what makes them work.


a. The z-index utilities

Tailwind provides a set of z-index utilities that cover the common layering needs.

UtilityCSS
z-0z-index: 0
z-10z-index: 10
z-20z-index: 20
z-30z-index: 30
z-40z-index: 40
z-50z-index: 50
z-autoz-index: auto
z-[100]z-index: 100 (arbitrary)

The default scale runs from 0 to 50 in steps of 10. The arbitrary value syntax allows any integer, and the parenthesis syntax allows a CSS variable.

<div class="relative z-10">Above</div>
<div class="relative z-20">Above that</div>
<header class="fixed top-0 z-50">Header</header>

The z-index property only applies to positioned elements. static elements ignore it. An element must be relative, absolute, fixed, or sticky for the value to take effect.

The z-auto value resets the property to its default. It is useful when overriding an inherited value.

A common mistake is to apply z-index to a static element and wonder why nothing happens. The fix is to add relative (or another position value) to the element.


b. What creates a stacking context

A stacking context is created by an element with specific properties. The most common are:

PropertyValueTailwind utility
positionrelative, absolute, fixed, sticky with z-index other than autorelative z-10
opacityLess than 1opacity-50
transformAnything other than nonescale-105
filterAnything other than noneblur-sm
isolationisolateisolate
mix-blend-modeAnything other than normalmix-blend-multiply
will-changetransform, opacity, or otherswill-change-transform
containlayout, paint, or strictcontain-layout
<!-- Creates a stacking context: position + z-index -->
<div class="relative z-10">
  <!-- Creates another: transform -->
  <div class="transform scale-105">
    <!-- z-index here is relative to the transform parent -->
    <div class="absolute z-50">...</div>
  </div>
</div>

Each of these properties creates a new stacking context, and any z-index inside that context is relative to the context’s siblings, not to the whole page.

The isolate utility is the explicit way to create a stacking context without applying a visual effect. It sets isolation: isolate, which does nothing visually but forces a new stacking context.

<div class="isolate">
  <div class="absolute z-50">Isolated inside</div>
</div>

The isolate utility is the correct tool when a component needs its own stacking context for its internal layering.


c. The stacking order within a context

Within a single stacking context, the elements are painted in a specific order, from bottom to top.

OrderElement
1The background and borders of the root element
2Descendant blocks in the normal flow (non-positioned)
3Descendant floats
4Descendant inline blocks and inline elements
5Positioned elements with z-index: auto or z-index: 0
6Positioned elements with a positive z-index, in ascending order

The layers are painted in this order. An element with a higher z-index appears above an element with a lower z-index, but only within the same context. A positioned element with a positive z-index is painted above all non-positioned content.

The practical consequence is that a positioned element with z-index: 1 is above all the normal flow content, even if the normal flow content has a higher z-index (which does nothing because the element is not positioned).

<div class="z-50">No effect, static</div>  <!-- not positioned -->
<div class="relative z-1">Above the flow</div>

The first element is not positioned, so its z-index is ignored. The second is positioned, so it appears above the first even though its z-index is lower.


d. The nesting rule

A stacking context is a self-contained unit. The elements inside it are painted as a group, and that group is placed in the parent context as a single unit.

<div class="relative z-10">
  <div class="relative z-100">Inner</div>
</div>
<div class="relative z-20">
  Outer
</div>

The z-100 inside the first context does not escape the context. The first context has z-10, and the second has z-20. The second context is above the first, so the entire first context — including the z-100 inner element — is below the second context. The inner z-100 only competes with the elements inside its own context.

This is the rule that explains most “z-index does not work” bugs. The inner element’s high z-index cannot escape its parent’s stacking context. To bring the inner element above the second context, the first context’s z-index must be raised, or the stacking context must be removed.

<!-- Fix: raise the parent context's z-index -->
<div class="relative z-30">
  <div class="relative z-100">Inner</div>
</div>
<div class="relative z-20">Outer</div>

Now the first context (z-30) is above the second (z-20), and the inner element appears above the outer element.


e. Common z-index problems and their fixes

The dropdown below the modal. A dropdown is inside a container with a transform (which creates a stacking context), and the modal is at the root. The modal’s context is above the dropdown’s context, so the dropdown appears below the modal. The fix is to raise the dropdown’s container above the modal’s context, or to move the dropdown out of the transformed container.

<!-- Problem -->
<div class="transform">
  <div class="relative z-50">Dropdown</div>  <!-- inside the transform context -->
</div>
<div class="fixed inset-0 z-40">Modal</div>  <!-- outside, above -->

<!-- Fix: raise the parent context -->
<div class="transform relative z-50">
  <div class="relative z-50">Dropdown</div>
</div>
<div class="fixed inset-0 z-40">Modal</div>

The sticky header above the modal. A sticky header with z-50 and a modal with z-50 are in the same context only if neither has an intermediate stacking context. If the modal’s parent has a transform, the modal’s z-50 is relative to that context, and the header’s z-50 may be above the whole modal. The fix is to ensure the modal’s context is above the header’s.

<!-- Problem -->
<header class="sticky top-0 z-50">Header</header>
<div class="transform">
  <div class="fixed inset-0 z-50">Modal</div>  <!-- below the header -->
</div>

<!-- Fix: raise the modal's context above the header -->
<header class="sticky top-0 z-40">Header</header>
<div class="transform relative z-50">
  <div class="fixed inset-0 z-50">Modal</div>
</div>

The tooltip behind the card. A tooltip inside a card with overflow: hidden is clipped by the card. The overflow: hidden does not create a stacking context, but it clips the tooltip. The fix is to move the tooltip outside the overflow container or to remove the overflow: hidden.

The badge behind the image. A badge with z-10 inside a relative container and an image with no z-index. The image is not positioned, so the badge’s z-10 places it above the image. If the image is inside a stacking context with a higher z-index, the badge may be below. The fix is to check the stacking contexts.


f. A layering scale for applications

Rather than using arbitrary z-index values, an application benefits from a small named scale that matches the layers.

Layerz-indexUse
Basez-0Page content
Raisedz-10Cards, popovers above content
Dropdownz-20Dropdown menus, selects
Stickyz-30Sticky headers, sidebars
Overlayz-40Modal backdrop
Modalz-50Modal content
Toastz-[60]Toasts, notifications

The scale is a convention. The important thing is that each layer is above the previous one and that the values are used consistently. A component that uses z-20 for a dropdown and another that uses z-15 for the same purpose creates confusion. A shared scale prevents the drift.

The z-[60] value for toasts is an arbitrary value because the default scale stops at 50. Alternatively, the theme can be extended to add a z-60 utility.


Complete Example Session

<!-- ============================================
PART 1: BASIC Z-INDEX
============================================ -->
<div class="relative z-10">Above</div>
<div class="relative z-20">Above that</div>
<!-- ============================================
PART 2: Z-INDEX REQUIRES POSITION
============================================ -->
<div class="z-50">No effect, static</div>
<div class="relative z-10">Positioned</div>
<!-- ============================================
PART 3: STACKING CONTEXT VIA POSITION + Z-INDEX
============================================ -->
<div class="relative z-10">
  <div class="absolute z-50">Inside the context</div>
</div>
<!-- ============================================
PART 4: STACKING CONTEXT VIA TRANSFORM
============================================ -->
<div class="transform scale-105">
  <div class="relative z-50">Inside the transform context</div>
</div>
<!-- ============================================
PART 5: STACKING CONTEXT VIA OPACITY
============================================ -->
<div class="opacity-90">
  <div class="relative z-50">Inside the opacity context</div>
</div>
<!-- ============================================
PART 6: ISOLATE FOR EXPLICIT CONTEXT
============================================ -->
<div class="isolate">
  <div class="absolute z-50">Isolated inside</div>
</div>
<!-- ============================================
PART 7: NESTING RULE
============================================ -->
<div class="relative z-10">
  <div class="relative z-100">Inner</div>
</div>
<div class="relative z-20">Outer</div>
<!-- Outer is above the entire first context -->
<!-- ============================================
PART 8: FIX BY RAISING THE PARENT CONTEXT
============================================ -->
<div class="relative z-30">
  <div class="relative z-100">Inner</div>
</div>
<div class="relative z-20">Outer</div>
<!-- Now Inner is above Outer -->
<!-- ============================================
PART 9: APPLICATION LAYERING SCALE
============================================ -->
<!-- Base: z-0, Raised: z-10, Dropdown: z-20 -->
<!-- Sticky: z-30, Overlay: z-40, Modal: z-50, Toast: z-[60] -->
<!-- ============================================
PART 10: MODAL ABOVE STICKY HEADER
============================================ -->
<header class="sticky top-0 z-30">Header</header>
<div class="fixed inset-0 z-50">Modal</div>

These ten parts cover basic z-index, the position requirement, stacking contexts via position, transform, and opacity, the isolate utility, the nesting rule, the fix by raising the parent context, the application layering scale, and a modal above a sticky header.


Quick Reference

Z-Index Utilities

UtilityCSS
z-0z-index: 0
z-10z-index: 10
z-20z-index: 20
z-30z-index: 30
z-40z-index: 40
z-50z-index: 50
z-autoz-index: auto
z-[100]z-index: 100
z-(--var)z-index: var(--var)

Stacking Context Creators

PropertyTailwind
position: relative/absolute/fixed/sticky + z-indexrelative z-10
opacity < 1opacity-50
transformscale-105, translate-x-4
filterblur-sm, brightness-50
isolation: isolateisolate
mix-blend-modemix-blend-multiply
will-changewill-change-transform
containcontain-layout

Painting Order Within a Context

OrderElement
1Root background and borders
2Non-positioned blocks
3Floats
4Inline blocks and inline elements
5Positioned with z-index: auto or 0
6Positioned with positive z-index

Application Layering Scale

LayerValueUse
Basez-0Page content
Raisedz-10Cards
Dropdownz-20Menus
Stickyz-30Headers
Overlayz-40Backdrop
Modalz-50Modal
Toastz-[60]Toasts

Debugging Steps

StepAction
1Find the stacking contexts
2Identify which context is on top
3Check if the element is inside a lower context
4Raise the context’s z-index or move the element
5Verify the element is positioned

Best Practices

✅ Do This:

<!-- Positioned elements for z-index -->
<div class="relative z-10">

<!-- Use isolate for explicit stacking contexts -->
<div class="isolate">

<!-- Use a consistent layering scale -->
<header class="sticky top-0 z-30">
<div class="fixed inset-0 z-50">

<!-- Raise the parent context to fix nesting -->
<div class="relative z-30">
  <div class="relative z-50">Inner</div>
</div>

❌ Don’t Do This:

<!-- z-index on a static element -->
<div class="z-50">  <!-- ❌ no effect -->

<!-- Escalating z-index values -->
<div class="z-[9999]">  <!-- ❌ sign of a stacking problem -->

<!-- z-index inside a transform without raising the parent -->
<div class="transform">
  <div class="relative z-50">  <!-- ❌ trapped inside -->
</div>

<!-- Mixing arbitrary values without a scale -->
<div class="z-[17]">  <!-- ❌ inconsistent -->

Common Pitfalls

PitfallWhy It HappensFix
z-index has no effectElement is staticAdd relative
Inner element below outerStacking context nestingRaise the parent context
Dropdown below modalParent has transformRaise the parent or move the element
Sticky header above modalDifferent contextsEnsure the modal’s context is higher
Tooltip clippedAncestor has overflow: hiddenMove the tooltip or remove the overflow
Inconsistent layeringNo shared scaleAdopt a named scale

Real-World Examples

1. Basic Stacking

<div class="relative z-10">A</div>
<div class="relative z-20">B</div>

2. Sticky Header

<header class="sticky top-0 z-30">Header</header>

3. Modal Overlay

<div class="fixed inset-0 z-50 bg-black/50">Modal</div>

4. Dropdown Menu

<div class="relative z-20">Dropdown</div>

5. Toast Notification

<div class="fixed bottom-4 right-4 z-[60]">Toast</div>

6. Isolated Component

<div class="isolate">
  <div class="absolute z-50">Internal layering</div>
</div>

7. Badge Above Card

<div class="relative z-10">
  <span class="absolute -top-1 -right-1 z-20">Badge</span>
</div>

8. Fix the Nesting

<div class="relative z-40">
  <div class="relative z-10">Raised context</div>
</div>

9. Transform Context

<div class="transform relative z-30">
  <div class="absolute z-50">Inside</div>
</div>

10. Full Screen Overlay

<div class="fixed inset-0 z-50"></div>

Visual

Stacking Contexts

┌──────────────────────────────────────────────────────────────┐
│  ROOT STACKING CONTEXT                                       │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Context A (z-10)                                      │  │
│  │  ┌──────────────────────────────────────────────────┐  │  │
│  │  │  Inner (z-100)                                   │  │  │
│  │  └──────────────────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │  Context B (z-20)                                      │  │
│  └────────────────────────────────────────────────────────┘  │
│                                                              │
│  Context B is above Context A, so it is above the Inner      │
│  element, even though Inner has a higher z-index.            │
└──────────────────────────────────────────────────────────────┘

Painting Order

┌──────────────────────────────────────────────────────────────┐
│  6. Positioned, positive z-index (top)                       │
│  5. Positioned, z-index auto or 0                            │
│  4. Inline blocks and inline elements                        │
│  3. Floats                                                   │
│  2. Non-positioned blocks                                    │
│  1. Root background and borders (bottom)                     │
└──────────────────────────────────────────────────────────────┘

The Nesting Fix

┌──────────────────────────────────────────────────────────────┐
│  BEFORE:                                                     │
│  Context A (z-10) contains Inner (z-100)                     │
│  Context B (z-20) is above Context A                         │
│  └── Inner is below Context B                                │
│                                                              │
│  AFTER:                                                      │
│  Context A (z-30) contains Inner (z-100)                     │
│  Context B (z-20) is below Context A                         │
│  └── Inner is above Context B                                │
│                                                              │
│  Raising the parent context raises everything inside it.     │
└──────────────────────────────────────────────────────────────┘

Application Layering Scale

┌──────────────────────────────────────────────────────────────┐
│  z-[60]  ──  Toast notifications                             │
│  z-50    ──  Modals                                          │
│  z-40    ──  Modal backdrop, overlays                        │
│  z-30    ──  Sticky headers, sidebars                        │
│  z-20    ──  Dropdowns, select menus                         │
│  z-10    ──  Raised cards, popovers                          │
│  z-0     ──  Base page content                               │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
z-index utilitiesz-0 through z-50, z-auto, arbitrary
RequiresA positioned element
Stacking contextCreated by position + z-index, transform, opacity, filter, isolate
Painting orderRoot, blocks, floats, inline, positioned, positive z-index
Nesting ruleA context is placed as a unit in its parent context
Common fixRaise the parent context’s z-index
Explicit contextisolate
Layering scaleBase, raised, dropdown, sticky, overlay, modal, toast
Debugging stepFind the context that is on top

Key takeaways:

  • z-index only applies to positioned elements. A static element ignores the value. The element must be relative, absolute, fixed, or sticky.
  • A z-index value only competes within its stacking context. The nesting rule means an element with z-index: 9999 can be below an element with z-index: 1 if the two are in different contexts and the higher context is on top.
  • Many properties create stacking contexts. position with z-index, transform, opacity less than 1, filter, isolation, mix-blend-mode, will-change, and contain all create contexts. The transform and opacity properties are the most common accidental creators.
  • The isolate utility creates a context explicitly. It sets isolation: isolate, which does nothing visually but forces a new stacking context for the component’s internal layering.
  • To fix a z-index problem, raise the parent context. If an inner element appears below an outer element, the inner element’s high z-index cannot escape its context. Raising the parent context’s z-index brings the entire context above the outer element.
  • A named layering scale prevents drift. Base, raised, dropdown, sticky, overlay, modal, and toast each have a z-index value. Using the scale consistently avoids the ad-hoc escalation that produces z-index: 9999 everywhere.
  • Arbitrary values are available when the scale is not enough. z-[60] or z-(--var) cover the cases where the default scale stops.

Remember: z-index is not a global ordering; it is a hierarchy of stacking contexts. The value only competes within its context, and the context is placed as a unit in its parent. Understanding which element creates a context and which context is on top is the whole of debugging z-index problems. The properties that create contexts are not all obvious: transform and opacity are the most common accidental creators. The isolate utility is the explicit tool. And a named layering scale keeps the values consistent across the application. When a z-index problem appears, the fix is usually to raise the parent context, not to increase the inner value.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!