
When z-index Doesn't Work
Understanding stacking context to fix z-index issues.

Understanding stacking context to fix z-index issues.
Tired of naming classes? Writing CSS directly inside HTML sounds ugly, but it became the world standard. Why?

Class is there, but style is missing? Debugging Tailwind CSS like a detective.

Think Android is easier than iOS? Meet Gradle Hell. Learn to fix minSdkVersion conflicts, Multidex limit errors, Namespace issues in Gradle 8.0, and master dependency analysis with `./gradlew dependencies`.

App crashed with TypeError? Learn why 'Null is not a subtype of String' happens and how to make your JSON parsing bulletproof with Zod/Freezed.

I created a Modal component and gave it z-index: 9999.
But it still appeared behind the Header (z-index: 10).
.modal {
z-index: 9999;
}
.header {
z-index: 10;
}
"Is 9999 not big enough? Should I try 999999?" I increased the number, but nothing changed. The modal was trapped behind the header.
My misconception was: "Higher z-index ALWAYS comes on top". I thought z-index was a global ranking system.
But CSS doesn't work like that. z-index only compares elements within the same Stacking Context.
I finally understood it with this analogy:
"Imagine a building with many rooms (Stacking Contexts)."In my case:
1 (Parent) < 10 (Header). The Modal's 9999 didn't matter because its parent was already lower.Most people think only position + z-index creates a context.
But modern CSS has many triggers.
position: relative/absolute/fixed + z-index (not auto).opacity less than 1.transform: scale/rotate/translate.filter: blur/contrast.will-change: transform.isolation: isolate (Explicitly creates a context)./* This creates a new Stacking Context! */
.card {
opacity: 0.99;
/* Now z-index inside .card is isolated from outside */
}
Don't fight the stacking context. Just move the Modal to the Root (document.body).
import { createPortal } from 'react-dom';
function Modal({ children }) {
return createPortal(
<div className="modal">{children}</div>,
document.body // Move directly to <body>
);
}
Now the Modal is a direct child of Body, so its z-index: 9999 competes globally.
Check the parent elements of your Modal.
Does any parent have overflow: hidden, opacity, or transform?
If so, try to remove those properties or move the Modal outside that parent.
Sometimes you Want to create a new context to reset z-index.
Use isolation: isolate.
.new-context {
isolation: isolate;
/* This explicitly starts a new stacking context */
}
This is safer than using transform: translateZ(0) hacks.
To truly master z-index, you must understand how Browsers render pages.
opacity: 0.9), the browser promotes it to a new Graphics Layer.The Trap: If "Parent A" is on "Layer 1" and "Parent B" is on "Layer 2" (higher). No matter what z-index the child of A has, it is physically painted onto Layer 1. It cannot jump out of Layer 1 to cover Layer 2. It's like drawing on a piece of paper; you can't draw "above" the paper above you.
Why transform creates a context?
Because transform often triggers GPU acceleration. The browser puts that element on a separate layer to rotate/scale it cheaply. Thus, a new Stacking Context is born.
Stop using Magic Numbers (9999, 99999).
Use a Sass Map or CSS Variables to manage layers systemically.
// _z-index.scss
$layers: (
'toast': 9000,
'modal': 8000,
'overlay': 7000,
'dropdown': 5000,
'header': 1000,
'base': 1
);
@function z($layer) {
@return map-get($layers, $layer);
}
// usage
.modal {
z-index: z('modal'); // 8000
}
This acts as a "Single Source of Truth" for your vertical layout.
Let's test your knowledge.
Scenario A:relative, z-index: 10absolute, z-index: -1opacity: 0.99, z-index: 999fixed, z-index: 1000transform: rotate(0deg), z-index: 999fixed, z-index: 1000transform makes fixed children behave like absolute (relative to the transformed parent, not the viewport). So the Child might be trapped inside A!Next time you are stuck, check this list:
position set? (Static elements ignore z-index).overflow: hidden, opacity, transform, filter).Understanding history helps you understand why CSS is weird.
<table> for layout. It was a dark time. No z-index needed because everything was a grid.float: left and clearfix. This is when z-index wars began.absolute became popular for precise control, leading to "z-index: 9999" hell.