What Is a Sticky Header?

A sticky header is a navigation bar that remains fixed at the top of the viewport as the user scrolls down the page. It improves usability by keeping navigation always accessible without the user needing to scroll back up.

💡 Tip: Use position: sticky instead of position: fixed. Sticky keeps the element in the document flow until it hits the threshold, while fixed removes it entirely — causing layout shifts.

Basic Sticky Header

The minimum CSS you need:

header {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: #ffffff;
}

That's it. top: 0 tells the browser where to "stick" — the element will scroll normally until it reaches 0px from the top of the viewport, then lock in place.

⚠️ Common mistake: position: sticky won't work if any ancestor element has overflow: hidden, overflow: auto, or overflow: scroll. Check your parent elements if it's not working.

Adding a Shadow on Scroll

A subtle shadow tells users visually that the nav is "floating" above the content. Add it with JavaScript listening to the scroll event:

header {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: #060b14;
  transition: box-shadow 0.3s ease;
}

header.scrolled {
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
}
window.addEventListener('scroll', () => {
  const header = document.querySelector('header');
  header.classList.toggle('scrolled', window.scrollY > 10);
});

Fixing z-index Issues

Your sticky header may disappear behind dropdowns, modals, or other positioned elements. Fix this by making sure your header has the highest z-index in the stacking context:

header {
  position: sticky;
  top: 0;
  z-index: 9999; /* higher than dropdowns, modals, etc */
}

If the header still disappears, check if any parent element creates a new stacking context (elements with transform, opacity < 1, filter, or will-change create new stacking contexts that isolate z-index).

Mobile Considerations

On mobile, a sticky header can consume valuable vertical space. Consider making it thinner on small screens:

header {
  position: sticky;
  top: 0;
  height: 64px;
}

@media (max-width: 640px) {
  header {
    height: 52px;
  }
}

Also consider using backdrop-filter: blur() to make the header feel less heavy on mobile while keeping it readable:

header {
  position: sticky;
  top: 0;
  background: rgba(6, 11, 20, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
✅ Result: A frosted-glass sticky header that stays readable over any background content — a popular modern design pattern.