GodUIGodUI
80

Anatomy of the Resizable Header

How one nav bar morphs from a wide rounded surface into a floating pill on scroll, and carries two independent layoutId tracks for hover and active state.

Most "shrinking header" implementations swap two separate DOM trees at a breakpoint. Resizable Header is one motion.nav the whole time — a single element whose class names change and whose box framer FLIPs between them, so the shrink reads as one continuous move instead of a cut.

Structurally it's a <header> wrapping a single motion.nav — a shrink-0 logo on the left, a link row in the middle with room for a hover pill and an active underline, and a shrink-0 CTA plus mobile toggle on the right:

Anatomyone nav · logo, links, CTA
Logo
brand mark, shrink-0 on the left
Links
nav row — carries the hover pill + active underline
CTA
shrink-0 action slot on the right
tsx
 
<motion.nav
  layout
  transition={spring}
  className={`... border border-border bg-background/70 backdrop-blur-xl ${
    shrunk
      ? "w-full max-w-2xl rounded-full px-3 py-2 shadow-lg"
      : "w-full max-w-5xl rounded-2xl px-4 py-3 shadow-sm"
  }`}
/>

Scroll crosses a threshold, the box morphs

useScroll tracks scrollY; a useMotionValueEvent listener flips a single boolean the instant it passes scrollThreshold (default 24px). That boolean is the only thing driving the two class strings above — the layout prop on motion.nav does the rest, FLIPping the box from max-w-5xl/rounded-2xl to max-w-2xl/rounded-full on a spring instead of snapping:

Scroll morphwide bar → floating pill
Expanded
max-w-5xl · rounded-2xl · scrollY ≤ threshold
Collapsed
max-w-2xl · rounded-full pill · past threshold
tsx
 
const { scrollY } = useScroll(scrollRef ? { container: scrollRef } : undefined);
useMotionValueEvent(scrollY, "change", (latest) => {
  setShrunk(latest > scrollThreshold);
});

const spring = { type: "spring", stiffness: 320, damping: 32, mass: 0.9 };

The same 320/32/mass 0.9 spring as Mega Menu's panel morph — both are "resize a real box" animations, and both want that extra mass so a wide surface doesn't overshoot visibly on the way to its new width.

The link row carries two independent motion.spans, each bound to its own layoutId: a hover pill that fills whatever link the pointer is over, and a thin active underline that only follows a click. They don't know about each other — the hover pill can sweep past three links while the underline sits still on the one that's actually active:

Two trackshover pill + active underline
Hover pill
layoutId span, fills the item under the pointer
Active underline
separate layoutId — tracks the clicked link only
tsx
 
{hovered === link.href && (
  <motion.span layoutId={`${ids.pill}-hover`} transition={spring} className="... bg-accent" />
)}
{isActive && (
  <motion.span layoutId={ids.pill} transition={spring} className="... bg-foreground" />
)}

Sticky by default, container-aware, mobile-safe

sticky (default true) pins the header with sticky top-0 z-sticky; set it false for a header that scrolls away normally. For layouts where the page itself isn't the scrolling element — a modal body, a split-pane panel — pass scrollRef and useScroll tracks that container's scroll position instead of the window's. Below md, the link row and CTA hide entirely behind a hamburger button that opens an AnimatePresence dropdown sharing the header's border radius and shadow language.

Accessibility

The active link carries aria-current="page", every link gets a focus-visible ring offset from the background so it reads clearly against the blurred surface, and the mobile toggle exposes aria-expanded so assistive tech knows the drawer's state without relying on the icon morph alone.

The result

Resultthe real component — scroll the panel below
northwind.com

Ship faster with Northwind

Scroll this panel — the nav springs into a compact, blurred pill and the active-link indicator rides along.

Analytics
Automations
Integrations
Security

Scroll it: one nav, one threshold, one spring — the bar becomes a pill and the two highlight tracks keep riding along underneath it.

On this page

Built with GodUI

Beautifully crafted motion components for modern interfaces.

Star on GitHub