← Back to all tutorials

Z - Index & Stacking Order

Control which elements appear on top with z-index — stacking contexts, stacking order, and common z-index patterns.

Z-Index & Stacking Order

When elements overlap, z-index controls which one appears on top. Think of it as a layer system — higher z-index = closer to the viewer.

The Default Stacking Order

Without any z-index, elements stack in this order (back to front):

  1. Root element background
  2. Non-positioned elements (in HTML source order)
  3. Positioned elements (in HTML source order)

Later elements in the HTML sit on top of earlier ones.

Using z-index

.element {
    position: relative;  /* z-index only works on positioned elements! */
    z-index: 10;
}

Important Rule: z-index Only Works on Positioned Elements

/* This does NOTHING: */
.element {
    z-index: 999;  /* Ignored! No position set */
}

/* This works: */
.element {
    position: relative;  /* or absolute, fixed, sticky */
    z-index: 999;
}

z-index Values

ValueBehavior
auto (default)Same as parent's stacking context
Positive (1, 10, 100)Above elements with lower z-index
0Creates a stacking context at the base layer
Negative (-1, -10)Behind non-positioned elements
.behind   { position: relative; z-index: -1; }
.base     { position: relative; z-index: 0; }
.above    { position: relative; z-index: 10; }
.on-top   { position: relative; z-index: 100; }

/* Visual stacking (back to front):
   .behind → .base → .above → .on-top */

Stacking Contexts

A stacking context is a group of elements that stack together as a unit. z-index values only compete within the same stacking context.

<div class="parent-a" style="position:relative; z-index:1;">
    <div class="child" style="position:relative; z-index:999;">
        I'm at z-index 999!
    </div>
</div>
<div class="parent-b" style="position:relative; z-index:2;">
    <div class="child" style="position:relative; z-index:1;">
        I'm only z-index 1, but I'm ON TOP!
    </div>
</div>

/* parent-b (z-index: 2) is above parent-a (z-index: 1).
   ALL children of parent-b are above ALL children of parent-a,
   regardless of their individual z-index values! */

What Creates a Stacking Context?

  • The root element (<html>)
  • position: relative/absolute/fixed/sticky with z-index other than auto
  • opacity less than 1
  • transform, filter, or perspective set to anything
  • isolation: isolate

A Common z-index Scale

:root {
    --z-dropdown: 100;
    --z-sticky:   200;
    --z-fixed:    300;
    --z-modal-backdrop: 400;
    --z-modal:    500;
    --z-popover:  600;
    --z-tooltip:  700;
}

.dropdown  { z-index: var(--z-dropdown); }
.header    { z-index: var(--z-fixed); }
.modal     { z-index: var(--z-modal); }
.tooltip   { z-index: var(--z-tooltip); }

Using a scale with gaps (100, 200, 300…) makes it easy to insert new layers later.

Debugging z-index

  1. Check that the element has a position value (not static)
  2. Check the parent's stacking context — a parent with a lower z-index limits all its children
  3. Use Chrome DevTools → 3D View (Layers panel) to visualize the stack
  4. Avoid z-index wars (99999) — use a systematic scale

Key Takeaways

  • z-index controls stacking order — higher values appear on top
  • Only works on positioned elements (relative, absolute, fixed, sticky)
  • z-index values only compete within the same stacking context
  • Parent stacking contexts limit their children's stacking
  • Use a systematic z-index scale with gaps for maintainability
  • Avoid z-index: 99999 — it's a sign of structural problems