Progress

Shows how close something is to done, like 3 of 5 steps.

Examples

Vacation fund$640 of $1,000
x
64%x

Shape

  • Vacation fund$640 of $1,000
    x

    bardefault

  • 64%x

    ring

Sizes

  • x

    sm

  • 64%x

    mddefault

  • 64%x

    lg

States

  • $0 of $1,000
    x

    empty

  • $640 of $1,000
    x

    part

  • $1,000 of $1,000
    x

    full

Usage

When to use it
Use
A savings goal, in a card or a list row.
Use
A budget spent so far this month, or a card limit used.
Use
A goal tile on a home screen, as a ring with the percentage inside.
Not for
Work that is running, such as a transfer or an upload. Use Status.
Not for
A value the person sets. Use Slider.

Rules

7 rules
Must
Pass label naming what is measured, such as "Saved toward Vacation fund". Because a meter with no name is read as a bare number with nothing to say what it measures.
Must
Pass valueText for money, such as "$640 of $1,000". Because without it a screen reader hears "64%" and never the amount.
Must
Show the figure as text beside a bar or inside a ring. Because the bar renders no text, so a sighted person guesses the amount from the fill length.
Should
Pass the real max and value, such as 640 of 1000, not a percentage you worked out.
Should
Leave the sm ring empty; 40px has no room for a figure, so put it beside.
Never
Use it for a task that is running; it has no indeterminate state. Because it is role meter, so a reader hears a fixed amount while nothing tells them work is moving.
Never
Recolor the fill per goal or turn it red past a limit; say over budget in words. Because the fill clamps at max, so a red bar cannot show how far over and fails color-blind readers.

Accessibility

4 checks
The root is role meter with aria-valuenow, aria-valuemin, aria-valuemax and the label as its name.
valueText becomes aria-valuetext, so money is read as money.
The lime fill has an olive edge, 3.95:1 on the page, so the filled part reads on grey.
The ring and the bar are hidden from assistive tech; the figure inside a ring is read as text too.

Edge cases

6 edge cases
Over the goal
The fill stops at full; say how much over in the text.
Nothing saved
Only the grey track shows.
Right to left
The bar fills from the right; the ring runs the other way.
Narrow container
The bar takes the width; the ring keeps its size.
Value changes
The fill moves over 200ms, at once under reduced motion.
max of 0
It draws empty instead of dividing by zero.

API

6 props
value, max
number default 0, 100
label
string
valueText
string
shape
'bar' | 'ring' default 'bar'
size
'sm' | 'md' | 'lg' default 'md'
children
ReactNode

Tokens

9 tokens
--surface-3
#e6e4df
--accent
#e4f222
--accent-line
#7c8700
--fg
#0f0f0d
--text-sm
14px
--text-lg
18px
--text-xs
12px
--dur-base
200ms
--ease-out
cubic-bezier(0.16, 1, 0.3, 1)

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.