StagesCard
When to use
Reach for StagesCard when a single operation expands into a known sequence of discrete sub-operations, and you want the user to see each one tick over rather than wait behind an opaque spinner. The canonical case is workspace provisioning during the new-entity onboarding flow — five stages (provision container, create vault, init audit log, wire assistant, add to portfolio) that complete in ~5 seconds.
Don't reach for StagesCard for an indeterminate progress bar — use Progress. Don't use it for a hierarchical operation tree — that's ThinkingTree.
Anatomy
- Header. Optional eyebrow, required title, optional status pill in the top-right.
- Stages list. One row per stage. Each row has a 14px circular dot (pending = empty ring; running = pulsing ring; done = filled green dot) + label + monospaced meta column on the right (revealed only when status === done, carrying the reference id; shows
…when running; blank when pending).
The list is rendered as an <ol> with aria-live="polite" so screen-reader users hear the transitions.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| eyebrow | React.ReactNode | — | Eyebrow above the headline. |
| titlerequired | React.ReactNode | — | Headline copy. |
| statusPill | React.ReactNode | — | Status pill or any other element rendered top-right. |
| stagesrequired | Stage[] | — | — |
| className | string | — | — |
| style | React.CSSProperties | — | — |
States
States live at the stage level, not the card level. Each stage moves through pending → running → done. The card itself is always rendered the same — the animation comes from per-stage status changes.
Tokens consumed
Done dots read --status-green filled with --status-green-13 halo. Running dots read the same green ring with the halo. Pending dots read --ink-12. Stage labels read --fg (running/done) or --fg-soft (pending). The meta column reads --fg-soft in Geist Mono.
Accessibility
- Keyboard interactions. None — passive surface.
- ARIA roles and properties.
<ol aria-live="polite">for the stages list. Each<li>carriesdata-statusfor testing + downstream selectors. - Focus order. Not focusable.
- Screen-reader expectations. Each stage transition is announced as the underlying data updates.
- Reduced motion. The running-state pulse should be disabled under
@media (prefers-reduced-motion)— wire this at the consumer's stylesheet level until it lands in the canonical motion-recipes. - Forced colors. Dots map to system colors as described above.