z-index usually stops working when the element is not being compared in the layer system you think it is. The fix is rarely a bigger number. The real fix is checking positioning, stacking context, overflow clipping, and parent structure.
CSS Layering Fix
Why Is My z-index Not Working?
z-index can feel broken when a dropdown hides behind a section, a modal appears under a header, or a tooltip disappears even with a huge value. But the browser is usually following CSS stacking rules correctly. The problem is almost always the context around the element, not the number itself.
- CSS stacking context
- Dropdown and modal bugs
- Overflow and parent layer issues
What the bug looks like
A dropdown hides under another section, a modal appears behind a header, a tooltip gets cut off, or a sticky element refuses to sit above the page.
Why it happens
z-index only compares elements within stacking rules. A parent can trap the child inside a lower layer, so a huge number does not always win.
What fixes it
Add the right positioning, inspect parent stacking contexts, remove accidental clipping, and move important overlays to a safer layer when needed.
The simple rule behind z-index bugs
z-index is not one global ladder where the biggest number always wins. It works inside stacking contexts. A stacking context is like a small private layer system inside the page.
This means a child with z-index:9999 can still appear behind another element if the child is trapped inside a parent stacking context that sits lower than a different parent.
The fastest fix is usually not z-index:999999. The fastest fix is finding which parent is controlling the layer battle.
The element has z-index but no positioning
This is the first thing to check. In many real layouts, z-index is expected to work on an element that has no positioning context. Add positioning intentionally before assuming the layer value is broken.
Broken code
No positioning.overlay {
z-index: 9999;
}
Broken visual result
Correct code
Positioned layer.overlay {
position: relative;
z-index: 20;
}
Fixed visual result
The dropdown is behind another section
Dropdowns often fail because the dropdown layer is lower than a nearby section, header, or card. The fix is not always a giant value. It is making the parent and dropdown layering intentional.
Broken code
Wrong layer order.nav {
position: relative;
z-index: 1;
}
.dropdown {
position: absolute;
z-index: 1;
}
.hero {
position: relative;
z-index: 5;
}
Broken visual result
Correct code
Intentional layer.nav {
position: relative;
z-index: 30;
}
.dropdown {
position: absolute;
z-index: 40;
}
.hero {
position: relative;
z-index: 1;
}
Fixed visual result
A parent stacking context traps the child
This is the bug that makes developers angry. A child has z-index:9999, but it still appears behind another element because its parent is inside a lower stacking context.
Broken code
Trapped child.parent-a {
position: relative;
z-index: 1;
}
.child {
position: absolute;
z-index: 9999;
}
.parent-b {
position: relative;
z-index: 2;
}
Broken visual result
Correct code
Fix parent order.parent-a {
position: relative;
z-index: 5;
}
.child {
position: absolute;
z-index: 10;
}
.parent-b {
position: relative;
z-index: 2;
}
Fixed visual result
The element is clipped by overflow, not hidden by z-index
Sometimes the element is not behind anything. It is being cut off by a parent with overflow:hidden, overflow:auto, or another clipping behavior. That is a different bug.
Broken code
Clipped overlay.card {
position: relative;
overflow: hidden;
}
.tooltip {
position: absolute;
top: 100%;
left: 0;
z-index: 9999;
}
Broken visual result
Card with overflow hidden
The tooltip exists, but the parent clips it.
Correct code
Visible overlay.card {
position: relative;
overflow: visible;
}
.tooltip {
position: absolute;
top: calc(100% + 8px);
left: 0;
z-index: 20;
}
Fixed visual result
Card with visible overflow
The overlay is allowed to appear outside.
A transform creates a new stacking context
A parent with transform can create a new stacking context. This often happens when people add transform:translateZ(0), animation helpers, GPU tricks, or subtle transform effects.
Broken code
Transform trap.wrapper {
position: relative;
transform: translateZ(0);
z-index: 1;
}
.menu {
position: absolute;
z-index: 9999;
}
.panel {
position: relative;
z-index: 2;
}
Broken visual result
Correct code
Avoid accidental context.wrapper {
position: relative;
}
.menu {
position: absolute;
z-index: 30;
}
.panel {
position: relative;
z-index: 1;
}
Fixed visual result
Fast practical rule
If z-index is not working, stop increasing the number and inspect the parent chain. The bug is usually positioning, stacking context, overflow clipping, or a parent with a property that created a new layer system.
Recommended baseline
Layering foundationUse small, intentional z-index values and create a clear layer scale instead of throwing giant numbers at every bug.
:root {
--layer-base: 1;
--layer-dropdown: 20;
--layer-sticky: 30;
--layer-modal: 40;
--layer-toast: 50;
}
.nav {
position: relative;
z-index: var(--layer-sticky);
}
.dropdown {
position: absolute;
z-index: var(--layer-dropdown);
}
.modal {
position: fixed;
inset: 0;
z-index: var(--layer-modal);
}
.toast {
position: fixed;
right: 16px;
bottom: 16px;
z-index: var(--layer-toast);
}
Why this baseline helps
Debug checklist
- Check whether the element has
position:relative,absolute,fixed, orsticky. - Inspect the parent chain for
transform,opacity,filter,perspective,isolation,contain, orwill-change. - Check whether a parent has
overflow:hidden,overflow:auto, or another clipping rule. - Confirm whether the issue is true layering or visual clipping.
- Compare parent stacking contexts, not only child z-index values.
- Avoid escalating from
z-index:10to999999without checking structure. - For modals and app-wide overlays, consider placing them near the end of the document or inside a dedicated layer root.
- Use a simple layer scale for dropdowns, sticky elements, modals, and toasts.
z-index:999999 while the element is trapped inside a lower parent context.
overflow:hidden makes many people think z-index is failing when the element is actually clipped.
When absolute positioning is the real problem
If an element is not only behind something but also appears in the wrong place, the issue may be absolute positioning. The element may be anchored to the wrong parent.
In that case, read Why is my absolute positioned element in the wrong place?.
When a dropdown is being cut off
If a menu or dropdown disappears at the edge of a card, section, slider, or container, the issue may be clipping rather than layer order.
In that case, read Why is my dropdown getting cut off?.
When fixed headers cover content
A fixed header can create another layer problem when it sits above content, anchors, modals, or dropdowns in unexpected ways.
If your sticky or fixed header is involved, read Why is my fixed header covering content?.
When overflow creates side effects
Overflow bugs can look like z-index bugs because they change what is visible on screen. But the solution is usually different.
If you are fighting overflow too, read Fix overflow causing horizontal scroll.
Final takeaway
When z-index is not working, the browser is usually not ignoring you. It is following stacking context rules that are easy to miss.
Start with positioning. Then inspect parent stacking contexts, overflow clipping, transformed wrappers, and the order of parent layers. Once the real layer system is clear, z-index becomes predictable again.
A clean parent structure beats a giant z-index number almost every time.