Components
Progress
Displays an indicator showing the completion progress of a task, such as a file upload or a multi-step form.
Basic usage
Pass a value between 0 and 100 to set how full the bar is.
Values
The indicator animates to the supplied value. An empty bar is 0 and a full bar is 100. The track keeps a --control-edge border so the 100% mark — and the control itself at value={0} — stays visible against the page.
Tones
tone recolours the bar and its track to the same ink step — the bar in the ink, the track a 20% tint of it — so every tone keeps the same fill-versus-track structure; default is --primary-ink. Those pairs were measured on the high-contrast palette, where they run 4.25:1 to 6.52:1. The default palette differs in light mode: there --primary-ink is --primary gold #E8B824, so default reads 1.55:1 against its track and 1.73:1 against the page — a recorded trade-off of the approved build, not a component defect. Use success for a finished job, warning as a quota approaches its ceiling, and destructive once it is over.
The warning tone also cuts diagonal hazard stripes through the filled part. That is not decoration: Comitor's brand is gold and warning is yellow, so --warning-ink and --primary-ink collapse to the same colour on three of the package's four palettes. The stripes carry the signal as shape, so it survives greyscale and colour blindness — never strip them off in a className override.
Do not darken the bar to pass the measurement. 3.13.0 tried exactly that — the letter avatar's gold-600 bar on a gold-100 track — and 3.13.1 took it back: in a real interface the dark brown bar weighed more than everything around it. The package now writes the rule down for every filled part, a bar, a background or an arc: it keeps the colour of the approved palette, and the 1.73:1 above is accepted, not a bug to fix. When a bar has to read more clearly, print its value beside it (as in “With a label” below), pick a tone, or leave it to the high-contrast palette, where the same bar clears 3:1. Do not repaint it through className either: bg-gold-600 is a raw scale step, which the high-contrast palette cannot move. How the two palettes split that work is on Colours.
Indeterminate
Leave value undefined while the total is still unknown. Radix marks the root data-state="indeterminate" and drops aria-valuenow; the package deliberately ships no sweep animation, so add one in your app or reach for Spinner when there is nothing to measure. For a ring instead of a bar — one whose indeterminate state does spin — use CircularProgress.
With a label
Progress bars carry no visible text on their own. Pair the bar with a heading and a percentage to give it context.
Variant helpers
progressTrackVariants and progressBarVariants are exported from @comitor/ui. Reach for them when you need a bar shape Radix cannot express — a stacked or segmented meter, for instance — so the custom markup inherits the same tokens instead of hand-picking colours.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | — | Current value, from 0 to max — the bar draws value / max, and a value outside that range is clamped (from 3.3.3), so past the limit is a full bar reading 100%. Omit it (or pass null) for the Radix indeterminate state, where the bar stays at 0. |
tone | 'default' | 'success' | 'warning' | 'destructive' | 'default' | Recolours both the bar and its track to the matching ink step — default is --primary-ink, the brand gold itself on the default palette. warning additionally cuts hazard stripes through the bar, so it stays distinguishable without colour. |
max | number | 100 | Maximum value the progress can reach. Inherited from the Radix Progress root. Before 3.3.3 the bar ignored it and drew value as a percentage while screen readers read value / max. |
getValueLabel | (value: number, max: number) => string | — | Builds the aria-valuetext string announced by screen readers. |
className | string | — | Additional Tailwind classes merged onto the track — e.g. set a custom height with h-3. |
...rest | ProgressPrimitive.Root props | — | All Radix Progress root props are forwarded to the underlying element. |
Accessibility
- Built on Radix Progress, the root element has
role="progressbar"witharia-valuenow,aria-valuemin, andaria-valuemaxset automatically. - Provide an accessible name with
aria-labeloraria-labelledbypointing at a visible heading so screen readers can announce what is loading. - The filled part is painted with
--primary-ink(or the tone's ink) rather than--primary, and the track carries a--control-edgeborder. That splits the two jobs WCAG 1.4.11 asks for: the border outlines the control against the page, the fill-versus-track pair carries the ratio. On the default palette the two gold tokens are the same colour, so thedefaultbar stays under 3:1 there by decision; the high-contrast palette is where it clears it. - Tone is never the only signal.
warningadds hazard stripes precisely because colour alone cannot separate it fromdefaulton the high-contrast and dark palettes (WCAG 1.4.1). - Stripes say “approaching the limit”, not what the limit is. Keep an
Alert, aStatusPill, or a line oftext-warning-inknext to the bar to spell out the threshold and the next step. - Avoid using a progress bar as the sole feedback for very long operations — pair it with a live region announcement when the value changes significantly.