Debugging z-index Wars: A Step-by-Step Guide
Systematic approach to diagnosing and fixing z-index conflicts including stacking context inspection and common anti-patterns.
Detailed Explanation
Debugging z-index Issues
z-index problems are among the most frustrating CSS bugs. Elements refuse to appear above others despite having higher z-index values. Here is a systematic approach to diagnosing and fixing these issues.
Step 1: Check for Stacking Contexts
The most common cause of z-index failures is an unexpected stacking context. Use browser DevTools to trace the stacking context tree:
Chrome: Elements panel > select element > check "Stacking Context" in the accessibility tree Firefox: Accessibility Inspector or use the 3D View
// Quick check in console: does an element form a stacking context?
function hasStackingContext(el) {
const style = getComputedStyle(el);
return (
style.position !== 'static' && style.zIndex !== 'auto' ||
parseFloat(style.opacity) < 1 ||
style.transform !== 'none' ||
style.filter !== 'none' ||
style.isolation === 'isolate' ||
style.willChange === 'transform' ||
style.willChange === 'opacity'
);
}
Step 2: Trace the Ancestor Chain
Walk up the DOM from the problematic element to the root. Identify every ancestor that creates a stacking context. The element's effective z-index position is determined by its nearest stacking-context ancestor, not by its own z-index value alone.
Step 3: Compare Sibling Contexts
z-index only compares elements within the same stacking context. If element A and element B have different stacking-context parents, you need to compare the parents' z-index values, not A and B directly.
Step 4: Check for Common Traps
/* Trap 1: Missing position */
.element {
z-index: 9999; /* has no effect without position! */
/* Fix: add position: relative; */
}
/* Trap 2: Parent has overflow: hidden */
.parent { overflow: hidden; }
.child { z-index: 9999; } /* visually clipped */
/* Trap 3: transform on ancestor */
.ancestor { transform: translateX(0); }
.descendant { position: fixed; z-index: 9999; }
/* fixed is now relative to ancestor, not viewport */
Step 5: Apply Fixes
/* Fix 1: Move the element to a portal */
/* Render at body level in React/Vue/Angular */
/* Fix 2: Use isolation: isolate on the correct ancestor */
.container { isolation: isolate; }
/* Fix 3: Restructure DOM to avoid stacking context conflicts */
/* Move the element out of the problematic ancestor */
Prevention
Define a z-index scale upfront (like with this tool), use CSS variables consistently, and add comments explaining stacking context boundaries in your CSS.
Use Case
When elements refuse to appear in the correct z-order despite having high z-index values, and you need a systematic approach to find and fix the root cause.