Scroll-aware Navbar
A navigation bar that condenses, hides, or widens as you scroll — three variants sharing one consistent rhythm, with nested scroll container support.
npx shadcn@latest add https://webberui.com/r/scroll-aware-navbar.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ScrollAwareNavbar />
Installation
npx shadcn@latest add https://webberui.com/r/scroll-aware-navbar.jsonOr, once registries are configured in components.json, install it as @webberui/scroll-aware-navbar.
Usage
import { ScrollAwareNavbar } from "@/components/ui/scroll-aware-navbar";
<ScrollAwareNavbar variant="shrink">
<span className="font-semibold">Webber</span>
<nav className="flex gap-4">
<a href="#features">Features</a>
<a href="#docs">Docs</a>
</nav>
<button className="rounded-full bg-neutral-900 px-3 py-1.5 text-white">
Get started
</button>
</ScrollAwareNavbar>The navbar aligns to the top of the scroll container with sticky top-0, and children is yours to lay out (justify-between by default). If the scrolling happens inside a nested overflow container, pass that container's ref to container:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[400px] overflow-y-auto">
<ScrollAwareNavbar variant="float" container={scrollerRef}>
{/* ... */}
</ScrollAwareNavbar>
{/* Content */}
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Navbar content, usually the brand, links, and a call-to-action button |
variant | "shrink" | "hide" | "float" | "shrink" | Scroll reaction style: condense, hide, or widen |
threshold | number | 24 | Threshold for counting as "scrolled" (px) |
expandedHeight | number | 72 | Expanded height at the top of the page (px) |
condensedHeight | number | 56 | Condensed height after scrolling (px), used by shrink and float |
pillWidth | number | 448 | Maximum pill width for the float variant at the top of the page (px) |
position | "sticky" | "fixed" | "sticky" | Positioning method; fixed means you must reserve space for the content yourself |
container | RefObject<HTMLElement | null> | — | Ref of the nested scroll container; when omitted, the whole window is tracked |
className | string | — | Applied to the visible navbar (the inner bar); use it to add max-w, font sizes, and so on |
wrapperClassName | string | — | Applied to the outermost positioning container (the header), for example to override the z-index |
aria-label | string | "Main navigation bar" | Accessible label for the navbar landmark |
The three variants
- shrink: transparent and floating above the content at the top of the page; once you scroll past
threshold, the height condenses fromexpandedHeighttocondensedHeightand a frosted-glass background with a bottom border fades in. - hide: the whole bar retracts with a displacement when scrolling down and slides back when scrolling up, which lets the content area take the full height while reading. A background appears after scrolling here too.
- float: at the top of the page it is a narrow centered pill (
pillWidth) with a gap from the top edge; after scrolling it widens to fill the container, the corner radius tightens, and it aligns to the top.
How it works
useScrolltracks the scroll position, and thehidevariant additionally compares successive displacements to determine direction; a 2px dead zone prevents jitter.- The
floatvariant's target widened width is measured from the outer container's actual width with aResizeObserver, so the widening animation is smooth and adapts to the container's size. - The background fades in and out as its own layer, so condensing or appearing never disturbs the content layout.
Accessibility
- The outermost element is a
<header>landmark with anaria-label; with thehidevariant, the moment keyboard focus moves into a retracted navbar it slides straight back, so focus never sits off screen. - When the user has "reduce motion" enabled at the system level, the scroll-down displacement hiding is disabled (the navbar is always visible) and the remaining states become instant switches rather than transitions.
- All
useScrollevent listeners and theResizeObserverare fully cleaned up on unmount.
Gooey Menu
A liquid, gooey expanding menu — click the floating trigger and the items are "pulled" out of the button through an SVG goo filter and fuse together, with four directions plus radial fanning.
Mega Menu
A large dropdown navigation menu whose panel height morphs smoothly to fit as you switch items, with hover and keyboard support.