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.

Try It — z-index Manager

Open full tool