Under The Hood
frontend css CSS 2.1 + modern compositing (Chrome 117+)

Stacking Contexts: isolation, will-change, and GPU Layers

Last updated
Prerequisites:
CSS positioning and z-index basics
Understanding of the box model and paint order
  • css
  • stacking-context
  • isolation
  • will-change
  • compositor
  • gpu
  • z-index

Read at your depth

The practical view

A stacking context is a group of elements painted together as a unit, in a fixed order relative to siblings. It is created by positioning + z-index, opacity < 1, transform, filter, mix-blend-mode, will-change, contain, and isolation: isolate. Inside a context, z-index only competes with elements inside the same context. To contain a sub-tree's z-index battles, use isolation: isolate on a parent — it creates a fresh context without needing z-index. This is the modern, predictable way to scope stacking instead of fighting z-index: 9999 wars.

The same idea in other frameworks

react equivalent
React components wrap modals/drawers in a portal to a top-level stacking context, relying on the same paint-order rules.

Legacy vs modern

Fighting z-index wars vs containing them with isolation

Global z-index escalation is fragile; isolation: isolate scopes the stacking battle to a subtree so inner values cannot leak outward.

before → after
Global z-index escalation
.modal { z-index: 9999; } /* ❌ OLD: global arms race — the next overlay needs 10000 */
isolation: isolate to scope stacking
.overlay-root { isolation: isolate; } /* ✅ NEW: fresh stacking context, no z-index needed */

Interview gotchas

Context

Interviewers ask this to see whether you understand that z-index is scoped by stacking contexts, not a global priority number — the classic production mystery.

The mechanical answer

z-index only orders elements within the same stacking context. If any ancestor of your element creates a stacking context (position + z-index, opacity < 1, transform, filter, will-change, contain, mix-blend-mode, isolation), then your huge z-index competes only against siblings inside that ancestor's context. The ancestor itself is then painted at its own z-index among ITS siblings. So a modal with z-index: 999999 inside a transformed card is trapped: the whole card sits at whatever level the card's context has. Fix: move the element to a top-level context (portal/body) or remove the creating property from ancestors, or contain with isolation at a deliberate root.

Trap

The naive answer is 'z-index is broken' or 'just use a bigger number'. Bigger numbers only matter within the same context. Another trap: assuming opacity: 0.99 or a tiny transform are harmless — they create stacking contexts too, which silently traps descendants. Candidates who trace the ancestor chain to find the context-creating property show real debugging skill.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.