Loader
Thinking affordances — the staggered three-dot loader and the shimmer status line, both SSR-stable and reduced-motion safe.
<Loader /><Shimmer>Thinking…</Shimmer>Usage
import { Loader, Shimmer } from "@reva/ui";Two thinking affordances for the pre-token window and streaming states:
- Loader — three staggered dots (opacity + a small bounce). The dots paint with
bg-currentand the root setstext-fg-muted, so a single text-colour class on the Loader retints all three dots at once. - Shimmer — a gradient sweep over status text ("Thinking…"). Built on Text (defaults
as="span",size="sm"; all Text props pass through exceptcolor— the gradient owns the ink, so the prop is omitted from its type). The sweep never drops below readable ink: the baseline is the muted foreground with a default-foreground highlight passing through, and both stops flip with the colour mode.
Both are SSR-stable: the DOM is identical server and client — only animation values branch — so they hydrate cleanly. And both are reduced-motion safe: Loader renders fully-visible static dots, Shimmer falls back to plain muted text via pure CSS (motion-reduce:), following the OS setting.
Examples
Sizes
{/* size: "sm" | "default" | "lg" */}<Loader size="sm" /><Loader /><Loader size="lg" />Retinting
A text-colour class retints the dots through bg-current — here the user-bubble pairing (fg/onSolid ink on the solid primary surface).
<div className="rounded-lg bg-primary-solid px-3 py-2"> <Loader className="text-primary-fg-onSolid" /></div>Shimmer status lines
Status copy that updates as the run progresses — swap the children, keep the sweep.
<Shimmer>Thinking…</Shimmer><Shimmer>Searching the client book…</Shimmer><Shimmer size="xs">Drafting the rebalance note…</Shimmer>Dark canvas
Both components resolve their ink from semantic tokens, so they repaint through the dark cascade with no per-component overrides.
<div className="dark rounded-lg border bg-canvas p-6"> <Loader /> <Shimmer>Thinking…</Shimmer></div>Accessibility
Loaderis arole="status"live region, and itsaria-labelprop renders as a visually-hidden text child (default "Loading") rather than anaria-labelattribute — live regions announce text content, not accessible names, so the sr-only text is what makes the Loader's appearance announceable (an empty labelled region typically isn't). One source, no double-naming. Announce-on-mount still varies by screen reader; when the announcement is critical, pair the Loader with visible status text (a Shimmer line) or manage an explicit announcement upstream.Shimmeris real text — readable, selectable, and announced like any other text. The gradient is presentation only; under reduced motion it renders as plain muted text.
Props
| Component | Prop | Type | Default | Description |
|---|---|---|---|---|
Loader | size | "sm" | "default" | "lg" | "default" | Dot size and gap. No children. |
Loader | aria-label | string | "Loading" | The status text — rendered visually hidden inside the live region (not as an attribute), so screen readers announce it. |
Loader | className | string | — | A text-colour class here retints all three dots (bg-current). |
Shimmer | as / size / weight / tracking / leading | Text props | as="span", size="sm" | All Text props pass through — except color, which is omitted because the gradient owns the ink. |
Reasoning
The collapsible thinking trace — auto-opens while reasoning tokens stream, auto-collapses once they settle, and hands control to the user on first toggle.
Code Block
The single highlighted-code surface — lazy shiki that costs nothing until code renders, line numbers, an enforced copy control, optional download, and a plain-text fallback for unknown languages.