Progress Narrator
A contextual narrator for progress journeys. You hand it an ordered list of milestones and a numeric current value; it picks the highest-threshold milestone that has been reached and announces it via a small fade-and-slide swap. The narrator stays silent until the first checkpoint is crossed.
Preview
Fresh start — your first checkpoint is up ahead.
checkpoint 0 / 4
Customize
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/progress-narrator.jsonUsage
import { ProgressNarrator } from "@craft-bits/core";
const MILESTONES = [
{ id: "warmup", threshold: 1, text: "First checkpoint cleared." },
{ id: "midway", threshold: 2, text: "Halfway there — the pattern is locking in." },
{ id: "home", threshold: 3, text: "Last lap." },
];
<ProgressNarrator milestones={MILESTONES} current={2} />Drive current from any progress source — completed steps, scroll depth, score:
const completed = useCompletedSteps();
<ProgressNarrator milestones={MILESTONES} current={completed.length} />Anatomy
- Live region — the outer wrapper is
role="region"witharia-live="polite". Each milestone swap is announced once without interrupting in-flight speech. - Milestone paragraph — the single active milestone, painted with the cb-accent left edge and a faint accent-muted backdrop. Keyed by
idinsideAnimatePresenceso swaps fade-and-slide. - Silent idle state — when no milestone has been reached yet, the region renders empty so the narrator can mount eagerly at the top of a page.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
milestones | ReadonlyArray<ProgressNarratorMilestone> | — | List of milestones, each with a stable id, a numeric threshold, and a text ReactNode. Order does not matter. |
current | number | — | Current progress value. Crossing a higher threshold swaps the announcement. |
regionAriaLabel | string | "Progress narration" | Accessible name for the live region. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root is a
role="region"witharia-live="polite", so screen readers announce each milestone swap without interrupting in-flight speech. - Override
regionAriaLabelwhen the surrounding domain language calls for it (e.g."Wave context","Lesson progress"). - Animation respects
prefers-reduced-motion— with the system flag set, the slide-in collapses to a pure opacity fade via the sameSPRINGS.smoothtransition.
Credits
- Extracted from:
AlgoFlashcards(src/lessons/primitives/chrome/ProgressNarrator.tsx). Replaced the project-specificuseProblemProgresslocalStorage subscription,RichText, and theuseId-driven collapsible chrome with a generic numeric-milestone API. The original picked between five hand-named states (first,mid-fresh,all-review,first-review,mid-review) keyed off a wave-slug array; the extracted version generalises that to threshold-based milestones so the same primitive can narrate scroll depth, score, completed-step count, or any other numeric progress signal.