Fix z-index not working

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.

Error 1

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

Number exists, layer does not win
Page section This section still appears above the overlay.
Overlay z-index alone is not a reliable setup.

Correct code

Positioned layer
.overlay {
  position: relative;
  z-index: 20;
}

Fixed visual result

Element can now layer correctly
Page section This section is now behind the overlay.
Overlay Positioning gives the z-index a useful layer role.
Error 2

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

Dropdown loses the layer battle
Navigation
Products
Pricing
Docs
Next section covers it

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

Dropdown is above the content
Navigation
Next section stays behind
Products
Pricing
Docs
Error 3

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

9999 is trapped inside parent A
Parent A z-index:1
Child z-index:9999
Parent B z-index:2

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 parent layer now wins
Parent B z-index:2
Parent A z-index:5
Child z-index:10
Error 4

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

Tooltip is cut off

Card with overflow hidden

The tooltip exists, but the parent clips it.

Tooltip z-index:9999, still clipped.

Correct code

Visible overlay
.card {
  position: relative;
  overflow: visible;
}

.tooltip {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 20;
}

Fixed visual result

Tooltip can escape

Card with visible overflow

The overlay is allowed to appear outside.

Tooltip is visible now.
Error 5

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

Transform created a layer trap
Transformed parent z-index:1
Menu z-index:9999
Neighbor layer z-index:2

Correct code

Avoid accidental context
.wrapper {
  position: relative;
}

.menu {
  position: absolute;
  z-index: 30;
}

.panel {
  position: relative;
  z-index: 1;
}

Fixed visual result

The overlay layer is free
Wrapper no longer traps the menu
Neighbor stays behind
Menu layer works predictably

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 foundation

Use 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

It avoids number chaos You do not need 999999 everywhere when the layer system is planned.
It makes intent obvious A dropdown, modal, sticky bar, and toast each get a predictable role.
It exposes real bugs faster When the scale is clear, a broken layer usually points to parent context or overflow.
It is easier to maintain Future changes become safer because the site has a known layering order.

Debug checklist

  • Check whether the element has position:relative, absolute, fixed, or sticky.
  • Inspect the parent chain for transform, opacity, filter, perspective, isolation, contain, or will-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:10 to 999999 without 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.
Best first move Add positioning intentionally and inspect the parent chain before changing the z-index number again.
Most common false fix Using z-index:999999 while the element is trapped inside a lower parent context.
Most overlooked cause overflow:hidden makes many people think z-index is failing when the element is actually clipped.
Better mindset z-index is not a power contest. It is a relationship between positioned elements, parent contexts, and layer order.

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.

Need more CSS fixes?

Browse the CSS cluster or jump back to the full FrontFixer library to keep debugging faster.

Leave a Comment