Layout Composition

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

The install command below needs a licence to run; without one it returns 401.

How to install Pro components →See the plans →

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add "https://webberui.com/r/hero-condense-header.en.json?t=<install token>"

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

240
56
0.5
Active edited usageDefault props
<HeroCondenseHeader/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add "https://webberui.com/r/hero-condense-header.en.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

PropTypeDefaultDescription
itemsHeroCondenseItem[]—Shared elements that migrate in sequence; array order is condense order
heroHeightnumber240Total hero height (px)
headerHeightnumber56Sticky header bar height (px)
condenseSpannumber0.5Share of the progress a single item's condense takes (0.05–1); smaller values make the staggering more obvious
containerReact.RefObject<HTMLElement | null>—Scroll container ref; required for a nested overflow container, defaults to the window
heroClassNamestring—Hero layer styling (background, corner radius, and so on)
barClassNamestring—Sticky bar container styling
childrenReact.ReactNode—The page content below the header
classNamestring—Appended to the outermost container's className

HeroCondenseItem

FieldTypeDescription
idstringUnique identifier, used to pair the hero anchor with the sticky slot
contentReact.ReactNodeShared element content (the same node migrates between the two ends)
heroClassNamestringHero anchor positioning (absolute); its height sets the expansion ratio and its center sets the landing point
headerClassNamestringSticky 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 offsetParent chain (layout coordinates before transforms), so it is unaffected by scrolling and sticky offsets. Position your anchors with layout properties such as left/top/inset/mx-auto and avoid transform utilities (for example -translate-x-1/2), which throw the measurement off
  • The whole migration is driven by transform alone and never triggers a layout reflow; a ResizeObserver re-measures whenever sizes or the window change
  • Each item tweens along its own progress window (condenseSpan wide, staggered against the others), so condensing and expanding happen one by one; the smaller condenseSpan is, 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