My modal had z-index: 9999. It was still behind the navigation bar. I tried z-index: 999999. Still behind. I tried z-index: 99999999 !important. Nothing.

I spent an hour increasing numbers before finally learning what was actually happening. The problem wasn't the number. The problem was stacking contexts.

Why Bigger Numbers Don't Help

Z-index only compares elements within the same stacking context. Think of it like folders in Photoshop.

If Folder A is above Folder B, everything in Folder A is above everything in Folder B—regardless of layer order inside those folders. A layer with z-index 9999 inside Folder B will still be below a layer with z-index 1 inside Folder A.

That's what was happening to my modal. It was trapped inside a container that had created its own stacking context, and that container was below the nav bar.

What Creates Stacking Contexts

Here's where it gets tricky. Many CSS properties create new stacking contexts, often unexpectedly:

.header {
    position: relative;
    z-index: 10;
}

.content {
    opacity: 0.99; /* Creates stacking context! */
    position: relative;
    z-index: 1;
}

.modal {
    position: absolute;
    z-index: 9999; /* Trapped inside .content */
    /* Will NEVER appear above .header */
}

The modal's z-index only matters compared to siblings inside .content. The whole .content block (z-index: 1) is below .header (z-index: 10), so the modal is stuck.

How to Fix It

Option 1: Move the Element

The cleanest solution: move your modal to be a direct child of <body>. In React, use a Portal. In Vue, use Teleport. Now it's in the root stacking context where z-index works as expected.

Option 2: Remove the Context

Find which parent is creating the trap. Inspect ancestors for transform, opacity, or filter. Sometimes these are added for performance (transform: translate3d(0,0,0) for hardware acceleration). If it's not essential, removing it dissolves the stacking context.

Option 3: DevTools Layers Panel

Chrome DevTools has a "Layers" panel (More tools → Layers). It shows your page in 3D—you can literally rotate and see which elements are stacking where, and why. It often labels "Stacking context created by..." which points directly to the culprit.

Manage Z-Index at Scale

Random numbers like 10, 50, 9999 scattered across files become unmaintainable. Use CSS variables to define a system:

:root {
    --z-dropdown: 100;
    --z-sticky-header: 200;
    --z-modal-backdrop: 300;
    --z-modal: 400;
    --z-toast: 500;
}

.modal {
    z-index: var(--z-modal);
}

Now all your layering logic is in one place. Need to insert something between header and modal? Change numbers once, not in fifty files.

Quick Debugging Steps

  1. Check if the element has position set (z-index needs it)
  2. Find which ancestor creates a stacking context
  3. Either move the element out or adjust the ancestor
  4. Use the Layers panel if you're stuck

Z-index issues are almost never browser bugs. They're misunderstandings of how CSS stacking actually works. Once you understand stacking contexts, you stop guessing numbers and start solving problems.

← Back to Debugging & Code Quality

Back to Home