caliper uidocs

Condensing header

Free

A page header that makes room as you read: the title shrinks into a bar, the description folds away and a border appears, following the scroll.

Installs components/ui/condensing-header.tsxDepends on none beyond ReactMotion honours reduced motion
pnpm dlx shadcn@latest add https://caliperui.site/r/condensing-header.json

Invoices

128 this year · €412,300 billed · 6 overdue
  • INV-2041€1,240
  • INV-2040€1,555
  • INV-2039€1,870
  • INV-2038€2,185
  • INV-2037€2,500
  • INV-2036€2,815
  • INV-2035€3,130
  • INV-2034€3,445
  • INV-2033€3,760
  • INV-2032€4,075
  • INV-2031€4,390
  • INV-2030€4,705
  • INV-2029€5,020
  • INV-2028€5,335
  • INV-2027€5,650
  • INV-2026€5,965
  • INV-2025€6,280
  • INV-2024€6,595

When to use #

Use it for

  • Long settings, record and list pages.
  • Phone screens where vertical space is short.

Skip it when

  • Short pages that never scroll.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/condensing-header.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { Plus } from "lucide-react"
import { useRef } from "react"
import { Button } from "@/components/ui/button"
import { CondensingHeader } from "@/components/ui/condensing-header"

export function CondensingHeaderDemo() {
  const scroller = useRef<HTMLDivElement>(null);
  return (
    <div ref={scroller} className="h-[420px] w-full max-w-md overflow-y-auto rounded-[14px] shadow-[inset_0_0_0_1px_var(--border)]" tabIndex={0} role="region" aria-label="Invoices page">
      <CondensingHeader scrollRoot={scroller} title="Invoices" description="128 this year · €412,300 billed · 6 overdue" actions={<Button size="sm"><Plus aria-hidden="true" />New</Button>} />
      <ul className="grid px-5 pb-6">
        {Array.from({ length: 18 }, (_, index) => (
          <li key={index} className="flex justify-between border-b border-border py-3 text-sm last:border-0">
            <span>INV-{2041 - index}</span>
            <span className="font-mono text-muted-foreground">€{(1240 + index * 315).toLocaleString("en-GB")}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

const actions = ["approved INV-2041", "commented on the Ostra deal", "matched 12 payments", "invited a teammate", "closed September", "exported the ledger", "changed a rule"];

API reference #

CondensingHeader

component
PropTypeDefaultDescription
title *string—
descriptionReactNode—
actionsReactNode—
distancenumber72Pixels of scroll over which it condenses.
scrollRootRefObject<HTMLElement | null>—The element that scrolls, if not the window.
classNamestring—

Accessibility #

It is built on native elements, so roles and focus come from the browser.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It checks prefers-reduced-motion itself and settles immediately instead of travelling. Every state is still reachable.