Topic 22 of 30
Z-Index & Stacking Context
Overview
Z-index controls the visual stacking order of positioned elements. Understanding stacking contexts is critical — a common bug is a modal with z-index: 9999 still appearing behind another element with a new stacking context.
Syntax
css
/* z-index only works on positioned elements */
.tooltip {
position: relative; /* or absolute, fixed, sticky */
z-index: 10;
}
/* What creates a new stacking context: */
/* 1. position + z-index (not auto) */
/* 2. opacity < 1 */
/* 3. transform */
/* 4. filter */
/* 5. isolation: isolate */
/* 6. will-change: transform */
/* Isolation prevents z-index leakage */
.modal-container {
isolation: isolate; /* creates new context */
}
/* Common z-index scale */
:root {
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-modal: 300;
--z-toast: 400;
--z-tooltip: 500;
}Common Pitfalls
- opacity, transform, filter, will-change all create NEW stacking contexts — your z-index values only work within that context.
- Higher z-index doesn't always mean on top — stacking context hierarchy matters more than z-index values.
- Interview tip: isolation: isolate creates a stacking context without any visual effect — the cleanest way to contain z-index scope.
Real-World Example
Fixing the classic 'modal behind sticky header' bug:
example
css
/* THE BUG: sticky header with transform creates stacking context */
.header {
position: sticky;
top: 0;
/* transform: translateZ(0); ← this creates a stacking context! */
/* Your modal (z-index: 999) will appear BEHIND this header */
}
/* THE FIX: Use z-index explicitly on header */
.header {
position: sticky;
top: 0;
z-index: 100; /* now participates in root stacking context */
}
.modal-backdrop {
position: fixed;
inset: 0;
z-index: 200; /* above header */
background: rgba(0, 0, 0, 0.7);
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 201;
}
/* Use isolation to contain a component's z-index scope */
.card-stack {
isolation: isolate;
}