Progress

Shows how far a task got.

Mode

  • Refund eval
    x

    progressdefault

  • Tokens
    x

    meter

Sizes

  • Refund eval
    x

    sm

  • Refund eval
    x

    defaultdefault

  • Refund eval
    x

    lg

States

  • Refund eval
    x

    indeterminate

  • Refund eval
    x

    partial

  • Refund eval
    x

    complete

  • Refund evalNear limit
    x

    warn

  • Refund evalAt limit
    x

    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
value
number | null default null
min, max
number default 0, 100
mode
'progress' | 'meter' default 'progress'
size
'sm' | 'default' | 'lg' default 'default'
warnAt
number | null default 0.8
failAt
number | null default 1
status
'warn' | 'fail'
statusLabel
ReactNode
ProgressValue 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

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.