|$ curl https://forge-ai.dev/api/markdown?path=docs/css/z-index
$cat docs/z-index-&-stacking.md
updated Yesterday·10 min read·published

Z-Index & Stacking

CSSZ-IndexLayoutIntermediate
Introduction

Z-index and stacking contexts control the visual layering of elements along the Z-axis — the axis coming out of the screen toward the viewer. Every element on a web page exists in a three-dimensional space, and z-index determines which elements appear on top of others when they overlap.

The z-index property seems simple — a higher number means closer to the viewer — but its behavior is governed by stacking contexts, which often cause unexpected layering issues. Mastering z-index requires understanding how stacking contexts are created and how they interact.

z-index-intro.css
CSS
1.box-a { z-index: 1; } /* lower value → further back */
2.box-b { z-index: 10; } /* higher value → closer to viewer */
The Z-Axis

The Z-axis runs perpendicular to the screen surface. Each element occupies a position on this axis determined by:

1.Whether the element is positioned (position other than static)
2.The element's z-index value (integer or auto)
3.The stacking context the element belongs to
4.The element's paint order (source order within the same stacking context)
z-axis.css
CSS
1.element {
2 z-index: 5; /* integer value */
3 position: relative; /* z-index only works on positioned elements */
4}
5
6/* z-index: auto (default) does not create a new stacking context */
7.auto { z-index: auto; }
8
9/* z-index accepts negative values */
10.behind { z-index: -1; }
📝

note

z-index only works on positioned elements (relative, absolute, fixed, or sticky) and on flex items or grid items. A static element ignores z-index entirely.
The z-index Property

The z-index property accepts integer values (positive, zero, negative) or auto. Higher values appear closer to the viewer. Negative values can position elements behind their parent or behind elements that do not have a z-index set.

z-index-values.css
CSS
1.value-auto { z-index: auto; } /* default — no stacking context created */
2.value-0 { z-index: 0; } /* creates stacking context when positioned */
3.value-1 { z-index: 1; }
4.value-999 { z-index: 999; }
5.value-neg { z-index: -1; } /* behind parent content */
6
7/* Practical range: -10 to 2147483647 */
8/* But nothing over 999 is really necessary if you manage contexts */
preview
Stacking Context

A stacking context is a group of elements that share a common parent and are painted together. The z-index of elements inside one stacking context only applies relative to other elements in the same context. When comparing elements from different stacking contexts, the parent context's z-index determines the order — not the child's individual z-index.

stacking-context-comparison.css
CSS
1.parent-a {
2 position: relative;
3 z-index: 1; /* creates stacking context */
4}
5
6.parent-b {
7 position: relative;
8 z-index: 2; /* creates stacking context — rendered on top of parent-a */
9}
10
11/* Even though child-of-b has z-index: 1, it renders above
12 child-of-a (z-index: 9999) because parent-b is above parent-a */
13.child-of-a {
14 position: absolute;
15 z-index: 9999;
16}
17
18.child-of-b {
19 position: absolute;
20 z-index: 1;
21}

warning

This is the most common z-index bug: setting a child's z-index to a very high number expecting it to appear above everything, not realizing a parent element belongs to a lower stacking context. The child is bounded by the parent's context.
Creating New Stacking Contexts

A new stacking context is created when any of the following conditions are met:

PropertyValueNotes
positionrelative, absolute, fixed, stickyOnly when z-index is not auto
opacityless than 1e.g. opacity: 0.99
transformnot nonetranslate, scale, rotate, etc.
filternot noneblur, brightness, contrast, etc.
isolationisolateCreates context regardless of other properties
mix-blend-modenot normalmultiply, screen, overlay, etc.
containlayout | paint | strictNewer contexts
will-changeopacity, transform, filterCreates context if value would
stacking-context-creation.css
CSS
1.stacking-context-examples {
2 /* All of these create a new stacking context */
3}
4
5.context-1 {
6 position: relative;
7 z-index: 0;
8}
9
10.context-2 {
11 opacity: 0.999;
12}
13
14.context-3 {
15 transform: translateX(0);
16}
17
18.context-4 {
19 filter: grayscale(0);
20}
21
22.context-5 {
23 isolation: isolate; /* explicitly creates context */
24}
25
26.context-6 {
27 mix-blend-mode: multiply;
28}

best practice

Use isolation: isolate to explicitly create a new stacking context without side effects. Unlike opacity: 0.999 or transform: translateX(0), it does not alter the element's visual appearance.
Stacking Order Rules

When elements overlap within the same stacking context, the painting order from back to front is:

1.Background and borders of the root element (html)
2.Elements with negative z-index (more negative → further back)
3.Non-positioned block-level elements in source order
4.Floated elements in source order
5.Non-positioned inline elements in source order
6.Elements with z-index: auto or z-index: 0 (positioned) in source order
7.Elements with positive z-index (higher value → closer to viewer)
preview
stacking-order.css
CSS
1.stacking-demo {
2 /* Within the same stacking context, higher z-index = closer to viewer */
3}
4
5.box-a { z-index: 1; } /* bottom */
6.box-b { z-index: 2; } /* middle */
7.box-c { z-index: 3; } /* top */
8
9/* Same z-index → source order determines layering */
10.box-d { z-index: 0; } /* rendered first — behind */
11.box-e { z-index: 0; } /* rendered second — on top */
Common Pitfalls

Pitfall 1: The high-z-index arms race

Developers keep increasing z-index values to fix layering issues, ending up with values like 99999. This is a symptom of stacking context mismanagement, not a solution.

pitfall-arms-race.css
CSS
1.modal { z-index: 99999; }
2.tooltip { z-index: 99998; }
3.dropdown { z-index: 99997; }
4/* Arms race — fragile, unmaintainable */

Pitfall 2: Fixed positioning and transforms

When a position: fixed element has a parent with a transform, the fixed element's containing block changes from the viewport to the transformed parent. This can cause z-index issues because the fixed element now belongs to a different stacking context.

Pitfall 3: opacity and stacking

Setting opacityto anything less than 1 creates a new stacking context. This means even if a child element has a high z-index, it will not appear above elements outside the parent's context.

pitfall-opacity.css
CSS
1.parent {
2 opacity: 0.99; /* creates new stacking context — unexpected! */
3}
4
5.child {
6 z-index: 100; /* still bounded by parent's context */
7}

info

Create a simple z-index scale system for your project. For example: dropdown: 100, sticky-header: 200, modal-backdrop: 300, modal: 400, tooltip: 500. Leave gaps to insert new layers without pushing everything up.
Debugging Stacking Contexts

Debugging z-index issues requires tracing which stacking context each element belongs to. Modern browser DevTools provide visualization tools for this.

debug-stacking.js
JavaScript
1// DevTools technique: inspect stacking layers in Chrome
2// 1. Open DevTools → Elements panel
3// 2. Select the element with z-index issues
4// 3. Check the "Layout" pane for "Stacking context"
5// 4. Look for "z-index" in the "Styles" pane
6
7// Programmatic stacking context detection
8function getStackingContext(el) {
9 const contexts = [];
10 let current = el.parentElement;
11 while (current) {
12 const style = getComputedStyle(current);
13 const zIndex = style.zIndex;
14 const position = style.position;
15 const opacity = parseFloat(style.opacity);
16 const transform = style.transform;
17 const filter = style.filter;
18
19 if (
20 (position !== 'static' && zIndex !== 'auto') ||
21 (opacity < 1) ||
22 (transform !== 'none') ||
23 (filter !== 'none')
24 ) {
25 contexts.push({
26 element: current,
27 reason: transform !== 'none' ? 'transform' :
28 filter !== 'none' ? 'filter' :
29 opacity < 1 ? 'opacity: ' + opacity :
30 'position: ' + position + ', z-index: ' + zIndex
31 });
32 }
33 current = current.parentElement;
34 }
35 return contexts;
36}

best practice

Firefox DevTools has the best visual stacking context inspector. Open the "3D View" to see the entire page rendered in three dimensions with stacking layers clearly separated. This makes it immediately obvious which elements sit above or below others and why.
Best Practices
Define a z-index scale system (e.g., 100-900 with gaps) and document it
Use isolation: isolate instead of hacky transforms or opacities to create stacking contexts
Keep z-index values within -10 to 100 — if you need 99999, you likely have a context problem
Remember that every CSS property that creates a stacking context bounds its children's z-index
Avoid setting z-index on the root stacking context (html/body level)
Use Firefox 3D View to visualize stacking issues
Document stacking contexts in complex components with comments
Prefer sibling-level z-index management over deeply nested z-index values
$Blueprint — Engineering Documentation·Section ID: CSS-21·Revision: 1.0