Progress
Shows how far a task got.
Mode
- Refund eval
progressdefault
- Tokens
meter
Sizes
- Refund eval
sm
- Refund eval
defaultdefault
- Refund eval
lg
States
- Refund eval
indeterminate
- Refund eval
partial
- Refund eval
complete
- Refund evalNear limit
warn
- Refund evalAt limit
fail
Usage
When to use it
- Use
- A task with a known end runs for more than a second: an eval, an upload, an index build.
- Use
- A level inside a range: tokens against a plan, seats used (mode="meter").
- Use
- A task with no count yet: value={null} slides a short segment along the track.
- Not for
- Where a run stands, not how far. Use Status.
- Not for
- A button's own action. Use Button.
- Not for
- Content loading into a known shape. Use Skeleton.
Rules
8 rules
- Must
- Use mode="meter" for a level in a range, and the default progress mode only for a task moving to done. Because a screen reader announces a progressbar as work under way, so tokens used read as a task that never finishes.
- Must
- Give every Progress a ProgressLabel, or aria-label when the words beside it already say what it is. Because a bar alone is announced as "progress bar, 40%" with nothing to say of what.
- Never
- Build a sliding or pulsing bar of your own for an unknown amount; pass value={null}. Because value={null} is the one sweep the motion foundation allows, and it stops as a half fill with the still arc under reduced motion, which a hand-made loop does not.
- Never
- Restyle the fill with bg-ink or another flat colour; the fill is small glaze run along the bar (glaze-sm glaze-sm-x). Because the fill is the same recipe as the slider's range and a ticked box, so a flat ink bar beside them reads as a different product.
- Should
- Use size="sm" (4px) in a row or a card footer, the default (6px) in a panel, and size="lg" (8px) only for the one task a screen is about. Because a thick bar in a dense row outweighs the label beside it.
- Never
- Colour a fill green, amber or coral by hand to decorate it; let warnAt and failAt or status do it. Because colour means status, and the thresholds add the words "Near limit" or "At limit" so the colour is never alone.
- Should
- Write the reading in the task's own units through ProgressValue's function: "120 of 200 tickets", "1.2 of 4.8 MB". Because a percentage hides how much is left, and the units say what is being counted.
- Should
- Replace a finished progress with the result, such as a Status, within a few seconds. Because a full bar left on screen reads as still working.
State order
5 states, highest first
- 1
- fail
- 2
- warn
- 3
- complete
- 4
- partial
- 5
- indeterminate
Accessibility
4 checks
- Progress is role="progressbar" and a meter role="meter", named by ProgressLabel.
- A screen reader hears the reading, then the status words: "92%, Near limit". On screen the glyph and the words come first, so the status never rests on hue alone. A statusLabel that is not a string is heard as the default words.
- Indeterminate reads "In progress"; under reduced motion the segment stands still as a half fill beside the still arc.
- The fill reads against the field track at 4.6:1 at its lightest (the start) in light and 7.6:1 at its darkest end in dark, past the 3:1 a graphic needs.
Edge cases
9 edge cases
- Value past max
- Clamps to full; a meter turns coral at failAt.
- Negative value
- Clamps to empty.
- max of 0
- Reads as empty instead of dividing by zero.
- Long label
- Truncates; the reading keeps its room at the end.
- Reduced motion
- The fill jumps instead of easing; indeterminate is a still half fill with the still arc.
- Tiny value
- Any value above zero shows at least a circle as wide as the bar is thick, so the round ends never squash.
- Threshold at the end
- failAt of 1 draws no tick: the end of the track is the limit.
- Right to left
- The fill, the ticks and the sweep run from the right.
- Forced colours
- The track takes an outline and the fill CanvasText.
API
9 props
valuenumber | nulldefaultnullmin, maxnumberdefault0, 100mode'progress' | 'meter'default'progress'size'sm' | 'default' | 'lg'default'default'warnAtnumber | nulldefault0.8failAtnumber | nulldefault1status'warn' | 'fail'statusLabelReactNodeProgressValue children(formatted, value) => ReactNode
Tokens
22 tokens
--progress-bar-sm- 4px
--progress-bar- 6px
--progress-bar-lg- 8px
--progress-sweep- 32%
--field- #efefef
--line-faint- rgb(5 5 5 / 0.06)
--line-strong- rgb(5 5 5 / 0.45)
--glaze-sm-top- #6b6b6b
--glaze-sm-mid- #3d3d3d
--glaze-sm-bottom- #232323
--glaze-sm-highlight- rgb(255 255 255 / 0.30)
--warn- #d98a0b
--fail- #e35e49
--warn-text- #8a5a00
--fail-text- #b23c29
--radius-pill- 999px
--type-label- 500 13px/1.35 var(--font-sans)
--type-caption- 400 12px/1.35 var(--font-sans)
--text-1- rgb(5 5 5 / 0.80)
--text-2- rgb(5 5 5 / 0.60)
--dur-state- 160ms
--dur-sweep- 1400ms