caliper uidocs

Prose

Free

Spacing, a readable measure and quiet links for long-form markup you do not control, such as a Markdown render, release notes or a legal page.

Installs components/ui/prose.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/prose.json

Release notes, October 2026

Eleven parts shipped, and every dialog now returns focus to where you were.

What changed

The toggle group moves with the arrow keys and is one tab stop. Install it with npx shadcn add @caliper/toggle.

Upgrade notes

  • Dialog actions are ordered cancel first, then the action
  • No breaking changes to props
We replaced the hand-rolled confirm in three places and deleted 400 lines.

When to use #

Use it for

  • Rendered Markdown, CMS content and release notes.
  • Docs that carry headings, lists, quotes and code together.

Skip it when

  • Interface text; style it with the type scale directly.
  • Marketing pages with their own layout; use the blocks.

Decisions #

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

Styling

Product 14/22 and reading 17/1.7
Product size matches the interface around it; reading size is the article template's, for pages people sit with.
Links stay underlined
Colour is never the only cue, and the underline thickens on hover so the target is clear without changing the text colour.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/prose.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 { useState } from "react"
import { Prose } from "@/components/ui/prose"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle"

export function ProseDemo() {
  const [size, setSize] = useState(["product"]);
  return (
    <div className="grid gap-5">
      <ToggleGroup type="single" label="Text size" value={size} onValueChange={setSize} className="justify-self-start">
        <ToggleGroupItem value="product" size="sm">Product</ToggleGroupItem>
        <ToggleGroupItem value="reading" size="sm">Reading</ToggleGroupItem>
      </ToggleGroup>
      <Prose size={size[0] === "reading" ? "reading" : "product"}>
        <h1>Release notes, October 2026</h1>
        <p>Eleven parts shipped, and every dialog now returns focus to where you were.</p>
        <h2>What changed</h2>
        <p>The <a href="#changes">toggle group</a> moves with the arrow keys and is one tab stop. Install it with <code>npx shadcn add @caliper/toggle</code>.</p>
        <h3>Upgrade notes</h3>
        <ul>
          <li>Dialog actions are ordered cancel first, then the action</li>
          <li>No breaking changes to props</li>
        </ul>
        <blockquote>We replaced the hand-rolled confirm in three places and deleted 400 lines.</blockquote>
      </Prose>
    </div>
  );
}

API reference #

Prose

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
size"product" | "reading""product"Product text is 14/22 for docs and release notes. Reading is 17/1.7 for long articles.

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 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.