Progress Bar
A thin horizontal bar that springs from its previous fill to a new target on every change. Five semantic tones, three sizes, and an indeterminate shimmer mode for when you don't know the endpoint yet.
Customize
Value
42
Tone
accent
Size
md
Mode
Installation
npx shadcn@latest add https://craftbits.dev/r/progress-bar.jsonUsage
import { ProgressBar } from "@craft-bits/core";
<ProgressBar value={42} />Leave value undefined for indeterminate mode:
<ProgressBar tone="accent" />Understanding the component
- Motion-value driven fill. A
useMotionValueholds the current percentage. On everyvaluechange,animate(mv, value, SPRINGS.smooth)springs the value toward the new target. The motion-value is mapped to a CSSwidthviauseTransform, so React doesn't re-render every frame — the spring drives the DOM directly. - Indeterminate via CSS keyframes. When
valueisundefined, a sub-strip (~40% of the track) slides from-100%to250%on a 1.4s loop viatransform: translateX. The@keyframesrule is wrapped in@media (prefers-reduced-motion: no-preference)so reduced-motion users see a static tone-tinted track — no JS branching. - Style injection is idempotent. The shimmer
@keyframesrule is appended to<head>once on first indeterminate mount, guarded by a sentinel<style id="cb-progress-shimmer-keyframes">. Subsequent mounts skip injection. - Tone-driven palette.
toneselects five semantic color combos (default,accent,success,warning,error). The track always usesbg-cb-bg-muted; only the fill picks up the tone — so the bar reads softly until there's progress to show. - Reduced motion. When
prefers-reduced-motion: reduceis set, the determinate fill snaps to its new target with no spring, and the indeterminate shimmer freezes via the CSS media query.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | undefined | Completion percentage in [0, 100]. Leave undefined for indeterminate mode. |
tone | 'default' | 'accent' | 'success' | 'warning' | 'error' | 'accent' | Semantic color for the fill. |
size | 'sm' | 'md' | 'lg' | 'md' | Track height (1px / 1.5px / 2.5px scale). |
transition | Transition | SPRINGS.smooth | Spring transition for the determinate fill. |
className | string | — | Merged onto the rendered <div>. |
Accessibility
- Renders with
role="progressbar"andaria-valuemin={0}/aria-valuemax={100}. Determinate bars additionally exposearia-valuenowrounded to an integer percent. Indeterminate bars omitaria-valuenowper ARIA spec. - The inner fill/shimmer is marked
aria-hidden="true"so assistive tech reads only the bar's accessible name (providearia-labeloraria-labelledbyfor context). - The indeterminate shimmer animation is automatically disabled when
prefers-reduced-motion: reduceis set — the keyframes are scoped under@media (prefers-reduced-motion: no-preference). The determinate spring snaps for the same users.
Credits
- Extracted from:
terminal-dreams(src/components/cookbook/ProgressBar.tsx).