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.
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.
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);
}