Hero Condense Header
As the page scrolls, the hero's title, avatar, and meta migrate one by one into a sticky header via shared-element FLIP, then expand back into place in order when you scroll up.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/hero-condense-header.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<HeroCondenseHeader />
Installation
npx shadcn@latest add "https://webberui.com/r/hero-condense-header.json?t=<install token>"Usage
Every entry in the items array is the same shared node migrating between the hero and the sticky bar. heroClassName positions its hero anchor (which determines its position and size when expanded), and headerClassName determines how it lays out in the sticky bar once condensed:
import {
HeroCondenseHeader,
type HeroCondenseItem,
} from "@/components/ui/hero-condense-header";
const items: HeroCondenseItem[] = [
{
id: "avatar",
content: <img src="/me.jpg" alt="Aria" className="h-full w-full rounded-full" />,
heroClassName: "inset-x-0 mx-auto top-9 h-16 w-16",
headerClassName: "order-1 h-8 w-8 shrink-0",
},
{
id: "title",
content: <span className="whitespace-nowrap text-sm font-semibold">Aria Nakamura</span>,
heroClassName: "inset-x-0 mx-auto top-[104px] h-7 w-56",
headerClassName: "order-2",
},
{
id: "meta",
content: <span className="whitespace-nowrap text-xs text-neutral-500">Product Designer</span>,
heroClassName: "inset-x-0 mx-auto top-[136px] h-5 w-64",
headerClassName: "order-3 ml-auto",
},
];
<HeroCondenseHeader items={items} heroHeight={240} headerHeight={56}>
<YourPageContent />
</HeroCondenseHeader>The scroll container can be the page itself (the window is the default scroll container) or any overflow-y: auto block — for the latter, pass that container's ref to container (as shown above).
Array order is condense order: the first item condenses first and expands last; the visual arrangement within the bar is controlled independently with order-* utilities.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | HeroCondenseItem[] | — | Shared elements that migrate in sequence; array order is condense order |
heroHeight | number | 240 | Total hero height (px) |
headerHeight | number | 56 | Sticky header bar height (px) |
condenseSpan | number | 0.5 | Share of the progress a single item's condense takes (0.05–1); smaller values make the staggering more obvious |
container | React.RefObject<HTMLElement | null> | — | Scroll container ref; required for a nested overflow container, defaults to the window |
heroClassName | string | — | Hero layer styling (background, corner radius, and so on) |
barClassName | string | — | Sticky bar container styling |
children | React.ReactNode | — | The page content below the header |
className | string | — | Appended to the outermost container's className |
HeroCondenseItem
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier, used to pair the hero anchor with the sticky slot |
content | React.ReactNode | Shared element content (the same node migrates between the two ends) |
heroClassName | string | Hero anchor positioning (absolute); its height sets the expansion ratio and its center sets the landing point |
headerClassName | string | Sticky slot layout; use order-* to adjust the order within the bar |
How it works
- The expansion ratio is
anchor height / slot height. The shared node takes its condensed (sticky bar) size as the baseline and scales up to hero size via transform — which means text color does not change during the migration, so pick a hero background that works with the sticky bar - Geometry at both ends is measured along the
offsetParentchain (layout coordinates before transforms), so it is unaffected by scrolling and sticky offsets. Position your anchors with layout properties such asleft/top/inset/mx-autoand avoid transform utilities (for example-translate-x-1/2), which throw the measurement off - The whole migration is driven by
transformalone and never triggers a layout reflow; aResizeObserverre-measures whenever sizes or the window change - Each item tweens along its own progress window (
condenseSpanwide, staggered against the others), so condensing and expanding happen one by one; the smallercondenseSpanis, the more obvious the staggering
Accessibility
- The shared text (name, job title) is rendered once and is always in the DOM, so assistive technology reads it normally; the hero layer's positioning anchors are empty and marked
aria-hidden - When the user has "reduce motion" enabled at the system level, a static sticky header (the condensed state) is rendered directly — no migration animation, no hero region — and it is still a usable header
- The sticky bar uses a semantic
<header>element, forming the page's banner landmark
Data View Morph
One dataset across list, table, board, and card layouts — every entry morphs continuously to its new position with a shared-element animation, preserving the user's spatial memory.
Drill Stack Panels
A layered drill-down detail panel system — new panels push in from the right, lower layers recede with parallax and dimming, and breadcrumbs pop back one level at a time with optional URL sync.