Sticky headers and sidebars
position: sticky
Chrome56
Edge16
Firefox59
Safari13
Features it needs
- Sticky positioningWidely available
These libraries measured scroll position and toggled position: fixed with a computed offset. position: sticky does it in the compositor, so it does not jitter and does not run code on scroll. Two things trip people up: sticky is relative to the nearest scrolling ancestor, so an overflow: hidden or overflow: auto anywhere up the tree silently breaks it, and you must set at least one of top, right, bottom or left.
When this applies
A header, sidebar or table head should stick while its container scrolls.
The native approach
.site-header {
position: sticky;
top: 0;
z-index: 10;
}
/* A sidebar that sticks until its section scrolls past. */
.sidebar {
position: sticky;
top: 5rem;
align-self: start;
}When the dependency is still right
An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.
- You need to know when the element became stuck, to add a shadow or shrink it. Nothing in CSS exposes that state directly, so this still wants an IntersectionObserver sentinel.
- You need the element to stick relative to the viewport while its parent has overflow set. Sticky is scoped to the nearest scroll container, so this needs a different structure.
- You need it to stop sticking at a computed point unrelated to its containing block.
Building it
This catalog stops at the swap. These guides go through the implementation and the fallbacks, and each one reads in the terminal through its own command.
- State aware sticky headers
npx -y modern-web-guidance@latest retrieve "state-aware-sticky-headers" - Scroll position aware elements
npx -y modern-web-guidance@latest retrieve "scroll-position-aware-elements"
Signs it was hand-rolled
No package is involved in any of these, so nothing would match in a package.json. If the code looks like one of them, this rule applies anyway, and the conditions above still decide.
- a scroll listener toggling a fixed class once an element passes an offset
- storing an element's original offsetTop so a placeholder can hold the gap when it goes fixed