caliper uidocs

Booklet

Free

A short ordered read, turned page by page: release highlights, a tour, a report. Turning a page moves it from the pile you have not read to the pile you have, so the piles show how far through you are while the counter tells you exactly.

Installs components/ui/booklet.tsxDepends on @radix-ui/react-slot, lucide-reactMotion none in component source
pnpm dlx shadcn@latest add @caliper/booklet

What's new in 2.4

Six changes in this release. Turn the page for each one.

1

Per-part stylesheets

Every part ships its own file. Dialog installs about 12 KB of CSS.

2

Listed in shadcn

Run shadcn add @caliper/<name>. No registry config needed.

3

Motion parts

Magnetic, DrawOn, CursorLabel and AnimatedList.

4

Depth set

Ten parts where depth carries order, magnitude or state.

5

Upgrading

Nothing to migrate. Edit the source you own.

6
Page 1 of 6

When to use #

Use it for

  • Release notes, short tours and reports of a few pages.
  • Content that is read in order and is finite.

Skip it when

  • Long documents, search results or anything people jump around in.
  • Content that needs a table of contents.

Decisions #

Why it is built this way, so you know what to keep when you change your copy.

Behaviour

The face you cannot see is inert
Tab never lands on a page that is turned away, and each page is an article named Page N of M.

Usage

Counter first
The piles are the felt sense of progress. The text counter and the Previous and Next buttons are the control.

Installation #

terminal
pnpm dlx shadcn@latest add @caliper/booklet

Adds its 5 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 { Booklet } from "@/components/ui/booklet"

export function BookletDemo() {
  const page = (title: string, text: string) => (
    <>
      <h4 className="m-0 text-[17px] font-bold leading-tight tracking-tight">{title}</h4>
      <p className="m-0 text-[12.5px] leading-normal text-muted-foreground">{text}</p>
      <div className="mt-1 h-2 rounded bg-[linear-gradient(90deg,var(--foreground)_0_34%,var(--muted)_34%_100%)]" />
    </>
  );
  return (
    <div className="w-full max-w-xl">
      <Booklet
        label="What is new in 2.4"
        pages={[
          page("What's new in 2.4", "Six changes in this release. Turn the page for each one."),
          page("Per-part stylesheets", "Every part ships its own file. Dialog installs about 12 KB of CSS."),
          page("Listed in shadcn", "Run shadcn add @caliper/<name>. No registry config needed."),
          page("Motion parts", "Magnetic, DrawOn, CursorLabel and AnimatedList."),
          page("Depth set", "Ten parts where depth carries order, magnitude or state."),
          page("Upgrading", "Nothing to migrate. Edit the source you own."),
        ]}
      />
    </div>
  );
}

API reference #

Booklet

component
PropTypeDefaultDescription
label *string—
pages *readonly ReactNode[]—One node per page. An odd count leaves the last page blank.
spreadnumber—Leaves turned, 0 to half the page count (rounded up). Controlled when set.
defaultSpreadnumber0
onSpreadChange((spread: number) => void)—
statusDepthStatus"ready"
messagesDepthMessages—
onRetry(() => void)—
disabledboolean—
classNamestring—

Accessibility #

It exposes group semantics, with aria-roledescription, aria-label, aria-live and aria-hidden kept in step with state. Changes that matter are announced through a live region.

KeyBehaviour
→Moves to the next item or value.
PageDownMoves by a larger step, or to the next page.
←Moves to the previous item or value.
PageUpMoves by a larger step, or to the previous page.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.