SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/sticky-navbars

7 CSS Sticky Navigation

4 Pure CSS3 with JSPublished 2026-10-05Free · no sign-up

Seven sticky navigation patterns: a bar that sticks to the top, one that hides while you scroll down, a header that shrinks once you leave the top, a translucent blurred bar, a scrollspy that highlights the current section, a mobile tab bar stuck to the bottom and a two-tier header where only the main nav sticks.

01. Sticky Navbar in a Scrolling Page

StickyNavbar

A navbar that stays at the top as the page scrolls, with position: sticky; top: 0 and a solid background so content does not show through. Because sticky stays in the flow, there is no gap or jump when it starts sticking.

View code
<div class="snb1" tabindex="0" role="region" aria-label="Page, scroll"><p class="hero">Hero area</p><nav aria-label="Main"><a href="#home">Home</a><a href="#menu">Menu</a><a href="#visit">Visit</a></nav><p>Content.</p><p>More content.</p><p>Even more content.</p></div>
.snb1 { width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border: 1px solid #e4e7ec; border-radius: 14px; background: #fff; font: 14px Inter, system-ui, sans-serif; }
.snb1:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb1 .hero { display: grid; place-items: center; height: 120px; margin: 0; background: #fef3c7; color: #92400e; font-weight: 700; }
.snb1 nav { position: sticky; top: 0; display: flex; gap: 14px; padding: 12px 14px; background: #fff; border-bottom: 1px solid #e4e7ec; }
.snb1 nav a { color: #101828; font-weight: 600; text-decoration: none; }
.snb1 nav a:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb1 p:not(.hero) { margin: 0; padding: 40px 14px; color: #475467; border-bottom: 1px solid #f2f4f7; }

02. Hides on Scroll Down, Shows on Up

StickyScroll direction

The navbar slides away when you scroll down to read and comes back the moment you scroll up, saving space on small screens. A scroll listener compares positions; the bar never hides while anything inside it has keyboard focus.

View code
<div class="snb2" tabindex="0" role="region" aria-label="Article, scroll"><nav aria-label="Main"><a href="#home">Journal</a><a href="#subscribe">Subscribe</a></nav><article><p>Scroll down and the bar hides.</p><p>Scroll up a little and it returns.</p><p>Reading space matters on phones.</p><p>Keep scrolling.</p><p>Almost there.</p><p>The end.</p></article></div>
.snb2 { position: relative; width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border-radius: 14px; background: #121828; font: 14px Inter, system-ui, sans-serif; }
.snb2:focus-visible { outline: 2px solid #a78bfa; outline-offset: 2px; }
.snb2 nav { position: sticky; top: 0; z-index: 1; display: flex; justify-content: space-between; padding: 12px 14px; background: #1e2546; transition: transform .25s ease; }
.snb2.up nav { transform: translateY(-100%); }
.snb2 nav a { color: #f8fafc; font-weight: 700; text-decoration: none; } .snb2 nav a + a { color: #c4b5fd; }
.snb2 nav a:focus-visible { outline: 2px solid #c4b5fd; outline-offset: 2px; }
.snb2 article { padding: 0 14px; color: #cbd5e1; } .snb2 p { margin: 0; padding: 40px 0; }
@media (prefers-reduced-motion: reduce) { .snb2 nav { transition: none; } }
var box = document.querySelector('.snb2'), nav = box.querySelector('nav'), last = 0;
box.addEventListener('scroll', function () { var y = box.scrollTop; if (!nav.contains(document.activeElement)) box.classList.toggle('up', y > last && y > 60); last = y; }, { passive: true });

03. Navbar That Shrinks When Scrolled

StickyShrink

A tall header with a large logo becomes a slim bar once you scroll past the top. A class toggled by an IntersectionObserver sentinel changes the padding and logo size, and transform keeps the logo shrink smooth.

View code
<div class="snb3" tabindex="0" role="region" aria-label="Page, scroll"><span class="sent" aria-hidden="true"></span><nav aria-label="Main"><a class="logo" href="#home">Atlas</a><a href="#trips">Trips</a></nav><p>Scroll a little.</p><p>The header gets smaller.</p><p>More content.</p><p>More content.</p></div>
.snb3 { width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border: 1px solid #e4e7ec; border-radius: 14px; background: #fff; font: 14px Inter, system-ui, sans-serif; }
.snb3:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb3 .sent { display: block; height: 1px; }
.snb3 nav { position: sticky; top: 0; display: flex; align-items: center; justify-content: space-between; padding: 22px 14px; background: #0f766e; transition: padding .25s ease, box-shadow .25s; }
.snb3 .logo { color: #fff; font-size: 26px; font-weight: 900; text-decoration: none; transform-origin: left center; transition: transform .25s ease; }
.snb3 nav a + a { color: #ccfbf1; font-weight: 600; text-decoration: none; }
.snb3.small nav { padding: 10px 14px; box-shadow: 0 6px 12px -6px rgba(0,0,0,.3); } .snb3.small .logo { transform: scale(.7); }
.snb3 nav a:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.snb3 p { margin: 0; padding: 40px 14px; color: #475467; }
@media (prefers-reduced-motion: reduce) { .snb3 nav, .snb3 .logo { transition: none; } }
var box = document.querySelector('.snb3');
new IntersectionObserver(function (es) { box.classList.toggle('small', !es[0].isIntersecting); }, { root: box }).observe(box.querySelector('.sent'));

04. Translucent Blurred Navbar

StickyGlass

A sticky bar with a semi-transparent background and backdrop-filter: blur, so colourful content passes underneath as a soft blur. The background stays opaque enough on its own for the links to keep contrast if blur is unsupported.

View code
<div class="snb4" tabindex="0" role="region" aria-label="Gallery, scroll"><nav aria-label="Main"><a href="#home">Prism</a><a href="#gallery">Gallery</a></nav><div class="c"><i style="--g:#f97316"></i><i style="--g:#06b6d4"></i><i style="--g:#a855f7"></i><i style="--g:#22c55e"></i></div></div>
.snb4 { width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border-radius: 14px; background: #fff; font: 14px Inter, system-ui, sans-serif; }
.snb4:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb4 nav { position: sticky; top: 0; z-index: 1; display: flex; justify-content: space-between; padding: 12px 14px; background: rgba(255,255,255,.78); -webkit-backdrop-filter: blur(12px) saturate(1.6); backdrop-filter: blur(12px) saturate(1.6); border-bottom: 1px solid rgba(16,24,40,.08); }
.snb4 nav a { color: #101828; font-weight: 700; text-decoration: none; }
.snb4 nav a:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb4 .c { display: grid; gap: 10px; padding: 10px; } .snb4 i { display: block; height: 120px; border-radius: 12px; background: var(--g); }

05. Scrollspy Highlights the Section

StickyScrollspy

The sticky nav highlights the link for the section currently in view and moves aria-current with it, so the highlight and the announced state agree. An IntersectionObserver with a band in the middle of the view picks the active section.

View code
<div class="snb5" tabindex="0" role="region" aria-label="Guide, scroll"><nav aria-label="On this page"><a href="#snb5a" aria-current="true">Plan</a><a href="#snb5b">Build</a><a href="#snb5c">Launch</a></nav><section id="snb5a"><h3>Plan</h3><p>Define the goal.</p></section><section id="snb5b"><h3>Build</h3><p>Make the first version.</p></section><section id="snb5c"><h3>Launch</h3><p>Ship and measure.</p></section></div>
.snb5 { width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border-radius: 14px; background: #121828; font: 14px Inter, system-ui, sans-serif; }
.snb5:focus-visible { outline: 2px solid #34d399; outline-offset: 2px; }
.snb5 nav { position: sticky; top: 0; display: flex; gap: 4px; padding: 8px; background: #121828; border-bottom: 1px solid #263048; }
.snb5 nav a { padding: 6px 10px; border-radius: 999px; color: #94a3b8; font-weight: 600; text-decoration: none; }
.snb5 nav a[aria-current] { background: #34d399; color: #052e16; }
.snb5 nav a:focus-visible { outline: 2px solid #34d399; outline-offset: 2px; }
.snb5 section { min-height: 220px; padding: 14px; } .snb5 h3 { margin: 0 0 4px; color: #fff; } .snb5 p { margin: 0; color: #94a3b8; }
var box = document.querySelector('.snb5'), links = box.querySelectorAll('nav a');
var io = new IntersectionObserver(function (es) { es.forEach(function (e) { if (!e.isIntersecting) return; links.forEach(function (l) { if (l.getAttribute('href') === '#' + e.target.id) l.setAttribute('aria-current', 'true'); else l.removeAttribute('aria-current'); }); }); }, { root: box, rootMargin: '-40% 0px -55% 0px' });
box.querySelectorAll('section').forEach(function (s) { io.observe(s); });
links.forEach(function (l) { l.onclick = function (e) { e.preventDefault(); var s = box.querySelector(l.getAttribute('href')); box.scrollTo({ top: s.offsetTop - 50, behavior: 'smooth' }); }; });

06. Sticky Bottom Tab Bar

StickyMobile

A mobile app tab bar that sticks to the bottom of the screen, with icons above short labels. Labels are always visible, because icon-only tab bars are harder to learn, and the bar includes safe-area padding for phones with a home indicator.

View code
<div class="snb6" tabindex="0" role="region" aria-label="App screen, scroll"><p>Feed item</p><p>Feed item</p><p>Feed item</p><p>Feed item</p><nav aria-label="App"><a href="#home" aria-current="page"><span aria-hidden="true">⌂</span>Home</a><a href="#search"><span aria-hidden="true">⌕</span>Search</a><a href="#saved"><span aria-hidden="true">♡</span>Saved</a><a href="#me"><span aria-hidden="true">☺</span>Profile</a></nav></div>
.snb6 { width: 260px; max-width: 100%; height: 280px; overflow-y: auto; border: 1px solid #e4e7ec; border-radius: 22px; background: #f9fafb; font: 12px Inter, system-ui, sans-serif; }
.snb6:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
.snb6 p { margin: 10px; padding: 30px 12px; border-radius: 12px; background: #fff; color: #475467; }
.snb6 nav { position: sticky; bottom: 0; display: grid; grid-template-columns: repeat(4, 1fr); padding: 6px 4px calc(6px + env(safe-area-inset-bottom)); background: #fff; border-top: 1px solid #e4e7ec; }
.snb6 nav a { display: grid; justify-items: center; gap: 2px; padding: 4px 0; color: #475467; font-weight: 600; text-decoration: none; }
.snb6 nav span { font-size: 18px; line-height: 1; }
.snb6 nav a[aria-current] { color: #6941c6; }
.snb6 nav a:focus-visible { outline: 3px solid #d6bbfb; outline-offset: -3px; border-radius: 8px; }

07. Top Bar Scrolls Away, Main Nav Sticks

StickyTwo-tier

A thin utility bar (phone number, language) scrolls away normally while the main navigation below it sticks. Only the second element is sticky, which is all it takes, so the page keeps its main links without wasting space on the utilities.

View code
<div class="snb7" tabindex="0" role="region" aria-label="Page, scroll"><div class="util">Call us · 020 7946 0000 · EN</div><nav aria-label="Main"><a href="#home">Harbour Hotel</a><a href="#book">Book</a></nav><p>Rooms</p><p>Dining</p><p>Spa</p><p>Location</p></div>
.snb7 { width: 280px; max-width: 100%; height: 280px; overflow-y: auto; border-radius: 14px; background: #0f172a; font: 14px Inter, system-ui, sans-serif; }
.snb7:focus-visible { outline: 2px solid #fbbf24; outline-offset: 2px; }
.snb7 .util { padding: 6px 14px; background: #020617; color: #94a3b8; font-size: 12px; }
.snb7 nav { position: sticky; top: 0; display: flex; justify-content: space-between; align-items: center; padding: 12px 14px; background: #1e293b; }
.snb7 nav a { color: #f8fafc; font-weight: 700; text-decoration: none; }
.snb7 nav a + a { padding: 6px 12px; border-radius: 8px; background: #fbbf24; color: #1c1917; }
.snb7 nav a:focus-visible { outline: 2px solid #fde68a; outline-offset: 2px; }
.snb7 p { margin: 0; padding: 40px 14px; color: #cbd5e1; border-bottom: 1px solid #1e293b; }

About these CSS Sticky Navigation

position: sticky first

position: sticky; top: 0 keeps a nav at the top without taking it out of the flow, so nothing jumps when it starts to stick. Give it a background so content does not show through.

Know when it is stuck

For a shadow or a smaller size once the bar is floating, watch a one-pixel sentinel above it with an IntersectionObserver and toggle a class when the sentinel leaves view.

Hiding needs care

A nav that hides on scroll down saves space on phones, but it must reappear on any scroll up and never hide while a link inside it has focus.

Scrollspy that tells the truth

When the nav highlights the section in view, move aria-current along with the highlight so screen readers hear the same thing people see.

Frequently asked questions

Why is my sticky nav not sticking?

Usually an ancestor has overflow set, or the nav is the last thing in a short parent with nowhere to travel.

Does a sticky nav cover anchor targets?

It can. Set scroll-padding-top on the scroller to the nav's height so jump links land below it.

Is backdrop-filter supported?

Yes in current browsers; keep a mostly opaque background so it still works without blur.