z-index for Fixed and Sticky Elements
How to handle z-index for fixed and sticky positioned elements, including common pitfalls with stacking contexts and scroll behavior.
Debugging
Detailed Explanation
Fixed and Sticky Element z-index
Fixed and sticky elements require special z-index consideration because they interact with the viewport and scrolling behavior. Understanding how they form stacking contexts prevents common positioning bugs.
Fixed Elements
.fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: var(--z-fixed); /* e.g., 1030 */
}
.fixed-sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: var(--z-fixed);
}
Sticky Elements
.sticky-nav {
position: sticky;
top: 0;
z-index: var(--z-sticky); /* e.g., 1020 */
}
.sticky-sidebar {
position: sticky;
top: 4rem; /* below the header */
z-index: calc(var(--z-sticky) - 1);
}
Common Pitfall: Fixed Inside Transform
The biggest trap with fixed elements:
/* BROKEN */
.page-wrapper {
transform: translateX(0); /* creates stacking context! */
}
.header {
position: fixed; /* now fixed relative to .page-wrapper, not viewport! */
z-index: 9999; /* doesn't help */
}
This happens because a parent with transform, filter, perspective, or will-change creates a new containing block for fixed-position descendants.
Fix Strategies
/* Strategy 1: Remove transform from ancestor */
.page-wrapper {
/* remove transform */
}
/* Strategy 2: Move fixed element outside the transformed ancestor */
/* In React: use a portal */
/* Strategy 3: Use sticky instead of fixed when possible */
.header {
position: sticky;
top: 0;
z-index: var(--z-sticky);
}
Multiple Sticky Elements
/* Layered sticky elements */
.top-bar {
position: sticky;
top: 0;
z-index: var(--z-sticky);
}
.sub-nav {
position: sticky;
top: 48px; /* height of top-bar */
z-index: calc(var(--z-sticky) - 1); /* below top-bar */
}
.section-header {
position: sticky;
top: 96px; /* height of top-bar + sub-nav */
z-index: calc(var(--z-sticky) - 2);
}
Recommended z-index for Fixed/Sticky
:root {
--z-sticky: 1020;
--z-sticky-secondary: 1019;
--z-fixed: 1030;
--z-fixed-sidebar: 1029;
}
Use Case
When building sticky headers, fixed sidebars, or layered sticky navigation that needs to interact correctly with other page elements and maintain proper z-ordering during scroll.