Feedback
Progress
A bar that fills, or sweeps when the end is unknown.
Usage
import { Progress } from '@misoto22/design'Notes
A bar that fills, or sweeps when the end is unknown.
Flat: a track in --stone, a fill in --ink. The White Reset has no gradient and no glow, so the only thing carrying the reading is the boundary between the two.
Anatomy
| Element | Description |
|---|---|
| Trackrequired | The Radix root: 4px of --stone at the pill radius, carrying role="progressbar" and aria-label from label. It is full width by default, so it takes the measure of whatever column it is dropped into. |
| Fill | The indicator, in --accent, sized by width rather than a translate so it grows from the inline start in a right-to-left document too. Present only when value is a number. |
| Sweep | What replaces the fill when value is null: a quarter-width --accent bar travelling the track on transform alone, mirrored under rtl so it never reads as progress running backwards. Under prefers-reduced-motion it stops where it is drawn rather than filling the track. |
| Value row | label on the left, a tabular-nums percentage on the right, above the track. It renders only when showValue is set and value is a number, which makes it the only place label is ever visible. |
| Column | The flex wrapper holding the row and the track. className lands here; every other prop is forwarded to the Radix root instead. |
Best practices
Do
- Pass showValue on any determinate bar. It is the only thing that puts label on screen — without it the name exists solely as aria-label, and a sighted reader is left with an unlabelled 4px rule and no number.
- Switch value back to null the moment the estimate stops being real. The fill transitions its width over --duration-slow, so a number that revises downward animates backwards and the reader watches progress undo itself.
- Set max to the real total rather than converting to a percentage yourself. The width and aria-valuemax come off the same number, so the picture and the announcement cannot drift — but a value past the ceiling still clamps, and an underestimated total parks the bar at full for the rest of the operation rather than admitting the estimate was wrong.
Don’t
- Do not pass a max that is not a positive number. Radix refuses it, prints its own warning and falls back to 100, and the width falls back with it — so value={40} paints and announces forty per cent of a ceiling nobody chose.
- Do not read the resting sweep as a position. Under prefers-reduced-motion it stops at a quarter of the track, which is what a determinate bar at 25% looks like — the announcement is the only thing that tells the two apart, and it is the reason omitting value matters.
- className styles the column, not the track, so a height utility passed that way stretches the wrapper and leaves the 4px bar exactly where it was.
Examples
determinate
A bar with a real fraction, and its name on screen. showValue is the only thing that prints label: without it the name exists solely as aria-label, and a sighted reader is left with an unlabelled 4px rule and no number. value is a percentage and nothing else — the component clamps to 0 and 100 in silence, so a total that was underestimated parks the bar at 100% for the rest of the operation rather than admitting the estimate was wrong. className styles the column, not the track, so a height utility passed there stretches the wrapper and leaves the 4px bar exactly where it was.
indeterminate
No value, so the bar sweeps instead of filling and aria-valuenow is dropped — a screen reader is told indeterminate rather than a number that is a guess. The value row only renders when value is a number, so the sweeping bar prints nothing at all and the name has to be on the page some other way, as it is here. Do not leave one of these up indefinitely: under prefers-reduced-motion the sweep rests as a full-width bar at 40% opacity, which is the shape of a bar that has finished, and a reader who asked for less motion is then looking at something that says it is done.
Rebuilding the search index
Started 40 seconds ago. No estimate yet.
bytes into percent
The quantity belongs to the caller; the percentage belongs to the bar. Divide at the call site, round it — the printed figure is rounded but aria-valuenow is whatever number you passed, so an unrounded division announces fifteen decimal places — and keep the real figures in the line underneath where the reader can read them. There is no way to hand this component a total: value is clamped to 100 whatever else is passed alongside it, so a bar told the total is 500 paints itself full and announces a hundred of five hundred, which is twenty per cent according to the only thing a screen reader can read. Switch value back to null the moment the estimate stops being real: the fill transitions its width, so a number that revises downward animates backwards and the reader watches progress undo itself.
18.2 MB of 29.4 MB
Accessibility
- Omitting value drops aria-valuenow, so a screen reader hears “indeterminate” rather than a number that is a guess.
- label is required — a bare bar announces nothing.
- The width is computed from value and max, the same pair Radix announces as aria-valuenow and aria-valuemax, so what is drawn and what is said cannot disagree.