Components
Progress bar.
A progress bar reports how much of a task is done and how much is left. It is passive — it never asks for input, it only reports a state. Use a determinate bar whenever a percentage is knowable; fall back to indeterminate only when it genuinely is not.
Colour
primary
neutral
info
success
warning
danger
ColourWhen to use
PrimaryThe default. Use for ordinary progress — an upload, a report generating.
NeutralLow emphasis — progress that is background information, not the focus of the screen.
InformationPairs with an informational message above the bar — a sync, a scheduled job.
SuccessThe step just completed, or progress toward a positive outcome.
WarningProgress that is behind pace or close to a limit — a quota filling up.
DangerProgress toward a failure state, or a retry attempt that keeps failing.
Size
Four track heights, from the Figma Height variant (4 / 8 / 12 / 16px). 8px is the default.
4px
8px (default)
12px
16px
Shape
Rounded is the Figma default — full radius on both ends. Square ends suit a bar set flush against a card or table edge.
Rounded — full radius. The everyday treatment.
Square — 2px radius. Flush against a container edge.
State
Determinate shows a real value. Indeterminate — not part of the Figma component — is a token-built addition for when the duration genuinely can't be known.
72%
Determinate — a known percentage. Always prefer this; it tells the user how long is left.
Indeterminate — duration unknown. The fill loops continuously; never shows a percentage.
Anatomy
60%
TrackFull width, var(--surface-3). Always visible — it shows the total the fill is measured against.
FillThe coloured portion, width proportional to value / max. Rounded ends by default, matching the track.
Height4, 8 (default), 12 or 16px. Thicker tracks read as more prominent, not more "done".
Value labelOptional 13px caption below the track, e.g. "72%". Omit it when the number is shown elsewhere on the screen already.
Do & don't
40%
Do — show the real value whenever one is knowable, and keep the caption plain — a percentage or a "step x of y".
Don't — reach for indeterminate just because computing the real percentage is inconvenient. Users trust a number more than a promise.
85%
Do — switch colour to match the stakes — warning as a quota nears its limit.
2%
Don't — use the thickest track for a bar that rarely moves past a sliver. Size should match how closely it's watched, not decorate the row.
Accessibility
role="progressbar"On the track element, not the fill — screen readers announce the role once, on the container that owns the value.aria-valuenow / min / maxDeterminate only. aria-valuenow updates live as the value changes; omit all three on an indeterminate bar rather than guessing a number.aria-label / aria-labelledbySay what is progressing — "Uploading file" — not just "Progress". A bare percentage with no label is meaningless out of context.ColourNever the only cue. Pair a status colour with the caption or the surrounding copy, the same rule as Alert and Badge.Reduced motionThe indeterminate loop honours prefers-reduced-motion and the in-app Motion: reduced setting — the fill freezes in place rather than animating, same mechanism as the skeleton loaders.Live updatesA determinate bar whose value changes while off-screen should sit in an aria-live="polite" region so screen reader users hear the update.Props (React)
No primitive ships yet — this is the target API if one is built, not something you can import today.
type ProgressBarProps = {
value?: number; // current amount
max?: number; // default 100
color?: "primary" | "neutral" | "info" | "success" | "warning" | "danger";
height?: 4 | 8 | 12 | 16; // default 8
rounded?: boolean; // default true
showValue?: boolean; // renders a "NN%" caption below the track
indeterminate?: boolean; // loops continuously; value/max are ignored
label: string; // required — feeds aria-label
};