A sticky header keeps your navigation within reach at all times, which sounds like a pure win. In practice, badly executed sticky header design eats a quarter of a phone screen, hides the exact heading a visitor just clicked, and introduces layout shift that quietly damages your Core Web Vitals.
This guide is the practical breakdown we use on client projects at J-A-B: recommended heights, scroll behaviors that feel natural, CSS that avoids anchor and layout-shift problems, accessibility requirements, and a side by side comparison of sticky navigation done well versus badly.
What a sticky header actually is (and how it differs from a fixed header)
A sticky header is a navigation bar that stays visible while the user scrolls. In CSS terms, there are two very different ways to achieve that, and choosing the wrong one is the source of most bugs:
| Property | Behavior | Impact on layout | Best for |
|---|---|---|---|
position: sticky |
Scrolls normally until it reaches its top offset, then pins |
Stays in the document flow, so no content jumps underneath it | Most sites, and the safest default |
position: fixed |
Removed from flow and pinned to the viewport immediately | Requires manual padding on the body, easy to get wrong | Headers that must overlay a hero or a full-height canvas |
Rule of thumb: start with position: sticky. Only fall back to fixed when the header genuinely has to float over content, and then reserve its height with padding on the page wrapper.

Ideal sticky header heights by device
The Nielsen Norman Group frames this as the content-to-chrome ratio: the persistent UI should consume as little vertical space as possible. On a phone, every 20 pixels of header is 20 pixels of article that the reader will never see.
Our working targets, measured as the pinned (scrolled) height, not the initial hero header:
| Viewport | Recommended pinned height | Maximum share of viewport | Notes |
|---|---|---|---|
| Mobile (up to 480px wide) | 48px to 56px | About 8 percent | Logo, one action, one menu button. Nothing else. |
| Tablet (481px to 1024px) | 56px to 64px | About 8 percent | Optional inline search if it does not push the CTA off screen |
| Desktop | 64px to 80px | About 10 percent | Full nav, CTA, language switcher |
| Landscape phone | 44px to 48px | About 12 percent | Consider unsticking the header entirely below 450px of height |
Two extra constraints people forget:
- Stacked bars kill mobile UX. A sticky header plus a cookie bar plus a promo strip plus a sticky "add to cart" bar can leave under half the screen for actual content. Budget one persistent element at the top and, at most, one at the bottom.
- Tap targets need room. Interactive elements should be at least 44 by 44 CSS pixels, so a 40px header is not physically possible without shrinking touch areas below accessible minimums.

Scroll behavior: pick a pattern, then commit to it
Motion is where sticky headers become annoying. Keep transitions short (150ms to 250ms), avoid bouncing, and never re-animate on every scroll tick.
| Pattern | How it works | Pros | Cons |
|---|---|---|---|
| Always visible | Header is pinned at all times | Predictable, zero JavaScript, best for apps and docs | Permanently consumes vertical space |
| Shrink on scroll | Tall on load, compact once pinned | Brand presence at the top, small footprint later | Height change can shift anchors if not handled with care |
| Hide down, show up (headroom) | Slides away when scrolling down, returns when scrolling up | Best content-to-chrome ratio on mobile, matches user intent | Needs a scroll threshold, otherwise it flickers |
| Appear after threshold | Not sticky at first, pins after the hero leaves the viewport | Clean above-the-fold, good for landing pages | If implemented with fixed, it is a classic CLS trap |
The hide-on-scroll rules that prevent motion sickness
- Only hide the header after the user has scrolled past roughly 2 header heights, never in the first 100 pixels.
- Require a delta of at least 5 to 10 pixels before switching state, so momentum scrolling does not toggle it.
- Always show the header instantly when scrolling up, even by a small amount.
- Force it visible at the very top of the page and when a menu or search panel is open.
- Respect
prefers-reduced-motionand drop the transition entirely for those users.
The CSS that makes sticky header design behave
1. A safe baseline
:root {
--header-h: 56px;
}
@media (min-width: 1024px) {
:root { --header-h: 72px; }
}
.site-header {
position: sticky;
top: 0;
z-index: 100;
block-size: var(--header-h);
display: flex;
align-items: center;
background: #fff;
/* keeps the bar readable over any content */
box-shadow: 0 1px 0 rgba(0,0,0,.08);
/* iPhone notch and Android gesture areas */
padding-inline: max(1rem, env(safe-area-inset-left));
}
2. Never let a sticky header cover anchor targets
This is the single most common complaint about sticky navigation. Click a table-of-contents link, land on the section, and the heading is hidden under the bar. Two lines of CSS solve it:
html {
scroll-padding-top: calc(var(--header-h) + 1rem);
}
/* belt and braces for individual targets */
[id] {
scroll-margin-top: calc(var(--header-h) + 1rem);
}
Use scroll-padding-top on the scroll container and scroll-margin-top on the targets. Both are supported everywhere that matters, and they also fix keyboard focus landing under the header. If your header shrinks on scroll, set --header-h to the pinned height so the offset is always correct after the animation ends.
3. Why your sticky header suddenly stopped sticking
Sticky positioning fails silently. Check these in order:
- An ancestor has
overflow: hidden,autoorscroll. This is by far the most frequent cause, often added to stop horizontal scrolling. - An ancestor has a
transform,filter,backdrop-filter,perspectiveorwill-change, which creates a new containing block. - The parent element is shorter than the scroll distance, so there is nothing to stick within.
- No
topvalue is set. Sticky without an offset does nothing. - The parent is a flex or grid container with
align-items: stretchresolving oddly. Setalign-self: starton the header.
To stop horizontal overflow without breaking sticky, use overflow-x: clip on the wrapper instead of overflow-x: hidden.
4. Mobile viewport units and the browser chrome
Mobile browser toolbars expand and collapse as you scroll. If a mega-menu panel uses 100vh, it will overflow. Use dynamic viewport units and subtract the header:
.mobile-menu-panel {
max-block-size: calc(100dvh - var(--header-h));
overflow-y: auto;
overscroll-behavior: contain;
}
5. Hide on scroll without a heavy scroll listener
.site-header {
transition: transform .2s ease;
}
.site-header.is-hidden {
transform: translateY(-100%);
}
@media (prefers-reduced-motion: reduce) {
.site-header { transition: none; }
}
const header = document.querySelector('.site-header');
let lastY = window.scrollY;
let ticking = false;
window.addEventListener('scroll', () => {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
const y = window.scrollY;
const delta = y - lastY;
if (y > 120 && delta > 8) header.classList.add('is-hidden');
if (delta < -8 || y < 120) header.classList.remove('is-hidden');
lastY = y;
ticking = false;
});
}, { passive: true });
Note the passive: true flag and the requestAnimationFrame throttle. Unthrottled scroll handlers are a real cause of poor Interaction to Next Paint scores on mid-range Android devices. There is a detailed walkthrough elsewhere.

Layout shift: how sticky headers damage Core Web Vitals
Cumulative Layout Shift is a Core Web Vital, and headers are a repeat offender because they sit at the very top of the page, above everything else. Every pixel of header movement pushes the whole document.
| Cause of shift | Fix |
|---|---|
JavaScript adds position: fixed after load |
Use CSS-only position: sticky, or reserve the height server side |
| Logo image without width and height | Set explicit width and height attributes, or use inline SVG |
| Web font swap changes nav item height | Fix the header height in CSS, preload the font, use font-display: swap with a matched fallback |
| Announcement or cookie bar injected above the header | Render it in the initial HTML, or overlay it rather than inserting it in flow |
Shrink-on-scroll animating height of an in-flow header |
Keep the outer height constant and scale inner elements, or animate only while pinned |
Mobile menu toggling overflow: hidden on body |
Compensate for scrollbar width with scrollbar-gutter: stable |
Does a sticky header affect SEO?
A sticky header is not a ranking factor in itself, and Google renders it like any other markup. The SEO impact is indirect but real:
- Core Web Vitals. Layout shift from the header and scroll handlers that hurt responsiveness both feed into page experience signals.
- Content visibility on mobile. A header that takes 25 percent of the screen plus a promo bar can look like an intrusive interstitial pattern. Keep persistent chrome slim and dismissible.
- Crawlable navigation. Header links must be real
<a href>elements. Buttons wired with JavaScript-only click handlers pass no link equity and may not be discovered. - Consistency between mobile and desktop. With mobile-first indexing, the mobile header is the one that counts. Do not strip important navigation links out of the mobile menu.
- Anchor and jump link usability. Featured snippets and Google’s "jump to" links can send users directly to a fragment. If your header hides the target heading, that visit starts with confusion. The
scroll-padding-topfix above handles it.

Accessibility checklist for sticky navigation
- Skip link first. Provide a visible-on-focus "Skip to content" link as the first focusable element, and give the main landmark the same
scroll-margin-topoffset. - Focus must never hide behind the bar. Test tabbing through the page. If the focus ring lands under the header, your scroll offsets are wrong.
- Reflow at 400 percent zoom. WCAG 2.2 success criterion 1.4.10 requires content to work at 320 CSS pixels wide. A tall sticky header at that zoom level can hide almost everything, so unstick it when the viewport height is small:
@media (max-height: 450px) { .site-header { position: static; } }. - Announce menu state. The mobile toggle should be a
<button>witharia-expandedandaria-controls, and Escape should close the panel and return focus to the toggle. - Contrast on transparent headers. If you use a translucent bar with
backdrop-filter, verify a 4.5:1 contrast ratio against the busiest background image on the page, not just the calm one. - Respect reduced motion. No slide, no shrink, no parallax for users who ask for less movement.
- Touch targets of 44px minimum, with enough spacing that a thumb does not hit the logo instead of the menu.
Sticky navigation done well versus badly
| Done well | Done badly |
|---|---|
| Documentation sites: a 56px bar with search and version switcher, plus a sticky sidebar that scrolls independently | Blogs with a 90px header, a newsletter strip and a share bar, leaving a narrow slot of text on a phone |
| Ecommerce: slim header hides on scroll down, returns on scroll up with cart and search always one tap away | Product pages where the sticky header covers the size selector after clicking an in-page anchor |
| Long-form articles: a 3px reading progress bar attached to a compact header | Headers that re-animate their shrink effect on every scroll tick, producing visible jitter |
| SaaS landing pages: header becomes sticky only after the hero, with a single primary CTA | Sticky mega-menus that open on hover and cover the page when a touch user taps by accident |
| Opaque or strongly blurred background so text underneath never bleeds through | Fully transparent headers over photography, unreadable half the time |

A quick QA checklist before you ship
- Measure the pinned header on a 360 by 800 viewport. Is it under 10 percent of the height?
- Click every table-of-contents and footnote anchor. Is the target fully visible?
- Tab through the page from the top. Does focus ever disappear?
- Run a Lighthouse or field CLS check with a slow connection and a cold cache.
- Open the mobile menu, scroll inside it, close it. Does the page scroll position stay put?
- Test at 400 percent zoom and in landscape on a short viewport.
- Confirm every navigation item is a crawlable link in the rendered HTML.
- Check the header with the cookie banner visible, since that is what most first-time visitors see.
FAQ
How do I make my header sticky?
Add position: sticky; top: 0; z-index: 100; to the header element and make sure no parent has overflow: hidden or a transform. That is the whole implementation for most sites, no JavaScript required. freshpies.co.uk has covered this at length.
What is a sticky header in web design?
It is a navigation bar that remains visible in the viewport while the user scrolls, so that menu, search and key calls to action are always reachable. It is also called a persistent header or fixed navigation bar.
Are sticky headers good or bad?
They are good when they are slim, quiet and readable. Research from the Nielsen Norman Group shows persistent navigation speeds up page switching, but the same research warns that oversized or animated bars reduce usable content and irritate users. The pattern is not the problem, the execution is. Related reading: How to Create a Sticky Header.
Do sticky headers hurt SEO?
Not directly. They can hurt indirectly through layout shift, slow scroll handlers, hidden content on mobile and navigation links that are not crawlable. Fix those and a sticky header is SEO neutral or slightly positive because it improves internal navigation.
What height should a sticky header be on mobile?
Between 48 and 56 pixels once pinned. That accommodates a 44px tap target while keeping roughly 92 percent of the viewport for content.
How do I stop the sticky header covering my anchor links?
Set scroll-padding-top on the html element, or scroll-margin-top on the target elements, equal to the pinned header height plus a small buffer. Avoid the old trick of invisible spacer elements, which adds markup and can confuse screen readers.
Should the header hide when scrolling down?
On mobile, usually yes: it reclaims the maximum amount of screen while keeping navigation one upward swipe away. On desktop, where vertical space is less scarce, an always visible slim bar is often the calmer choice.
Need a second opinion on your header?
If your site already has a sticky header and you are unsure whether it is helping or quietly costing you conversions, a short audit of heights, scroll behavior, anchor offsets and Core Web Vitals usually finds two or three quick wins. Get in touch with the J-A-B team and we will take a look.
