caliper uidocs

Scroll timeline

Free

A history down the page: the line fills as you scroll and each entry lights and settles as the line reaches it.

Installs components/ui/scroll-timeline.tsxDepends on none beyond ReactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/scroll-timeline.json

Scroll the story.

  1. March 2024

    Founded in Dublin

    Three people and a spreadsheet that took a week to close the month.
  2. October 2024

    First bank feed

    Transactions arrive every hour instead of from a CSV.
  3. May 2025

    Multi-entity close

    Groups close every entity in one place, with eliminations.
  4. January 2026

    1,000 teams

    Finance teams in 31 countries close with us.
  5. October 2026

    Any-currency matching

    Payments match invoices across currencies at the day's rate.

When to use #

Use it for

  • Company history, release history, case studies.
  • Order or project histories read top to bottom.

Skip it when

  • Event logs people scan; use the timeline or a table.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/scroll-timeline.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 { ScrollTimeline } from "@/components/ui/scroll-timeline"

const history = [
  { id: "h1", date: "March 2024", title: "Founded in Dublin", body: "Three people and a spreadsheet that took a week to close the month." },
  { id: "h2", date: "October 2024", title: "First bank feed", body: "Transactions arrive every hour instead of from a CSV." },
  { id: "h3", date: "May 2025", title: "Multi-entity close", body: "Groups close every entity in one place, with eliminations." },
  { id: "h4", date: "January 2026", title: "1,000 teams", body: "Finance teams in 31 countries close with us." },
  { id: "h5", date: "October 2026", title: "Any-currency matching", body: "Payments match invoices across currencies at the day's rate." },
];

export function ScrollTimelineDemo() {
  return (
    <div className="h-[420px] w-full max-w-md overflow-y-auto rounded-[14px] p-6 shadow-[inset_0_0_0_1px_var(--border)]" tabIndex={0} role="region" aria-label="Company history">
      <p className="mb-6 text-sm text-muted-foreground">Scroll the story.</p>
      <ScrollTimeline items={history} />
      <div className="h-48" aria-hidden="true" />
    </div>
  );
}

API reference #

ScrollTimeline

component
PropTypeDefault
items *readonly ScrollTimelineItem[]—
classNamestring—

Accessibility #

It is built on native elements and keeps aria-hidden in step with state.

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

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.