Progress
Show how far a task has progressed, or that work is still running.
import { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use Progress for measurable work, such as an upload or migration. Pass value={null} when progress is not yet known. Use a Spinner for short, unmeasured waits, or a Slider for values people adjust themselves.
import { Progress, ProgressLabel, ProgressValue } from '@youj/react'
export function Upload({ percent }: { percent: number }) {
return (
<Progress value={percent}>
<ProgressLabel>Uploading hero@2x.png</ProgressLabel>
<ProgressValue />
</Progress>
)
}Progress renders the track and the bar for you. Put a ProgressLabel and a ProgressValue inside it: they share the first line, and the track wraps below them. Without a visible label, give Progress an aria-label.
Description
File uploads that advance chunk by chunk, each with a name and a percentage.
A determinate bar, a finished one, an indeterminate one and one with a custom format.
value runs from min to max, 0 to 100 by default. ProgressValue shows the percentage, or takes a function to write its own text. format takes Intl.NumberFormat options for both the readout and the spoken value, and getAriaValueText lets the spoken text say more, such as the total.
The bar fills in the accent in Classic and in a neutral ink in the Bezel look, which keeps the accent for emphasis. A tone (accent, neutral, success, warning or danger) pins the fill in every style. size (sm, default or lg) sets the bar's thickness.
Progress renders its own track and indicator after its children, so a label and a readout sit above the bar. ProgressTrack and ProgressIndicator are exported for building your own bar inside Base UI's Progress.Root. They need that root, so they don't work outside it.
A deploy that uploads in chunks, then finalizes on an indeterminate bar, then goes live.
Pass null as soon as what's left can't be measured, like a deploy finalizing after its files upload. The bar hands over to the sweep, and ProgressValue shows nothing until there's a number again.
A render whose frames light up as fine ticks, and an encode step waiting on a traveling wave.
variant="ticks" draws the track as a row of fine vertical ticks, like a ruler or a level meter. They light from the inline start as the value grows; the tick at the edge lights partway and the next few warm up a little before the edge reaches them. With value={null} a wave of lit ticks travels from the start to the end, then rests. ticks sets how many there are (40 by default), and size sets their length. Everything else is the same progress: the role, the value, the label and the readout.
Code
progress-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propstone, variant, size, ticks (4)
Props
Progress
| Prop | Type | |
|---|---|---|
tone | ProgressTone'accent''neutral''success''warning''danger'The fill. Unset, the component style decides: the accent in Classic, a neutral ink in Bezel (which keeps the accent for emphasis). A tone pins it in every style. | – |
variant | 'default' | 'ticks'
default 'default' | 'default' |
size | Extract<Size, 'sm' | 'default' | 'lg'>The track's height: the bar's thickness, or the ticks' length. default 'default' | 'default' |
ticks | numberHow many ticks the default 40 | 40 |
Other props pass through: extends ProgressPrimitive.Root.Props. | ||
It takes Base UI's value (a number, or null for indeterminate), min, max, format, locale and getAriaValueText.
| Data attribute | Values |
|---|---|
data-variant | default, ticks |
data-size | sm, default, lg |
data-tone | accent, neutral, success, warning, danger (unset: the style decides) |
data-indeterminate, data-progressing, data-complete | Base UI's state |
ProgressValue
ProgressValue adds no props of its own. They pass through to ProgressPrimitive.Value.Props.
Its children can be a function of the formatted value and the raw value, (formatted, value) => ReactNode.
MotionValue change, indeterminate sweep · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Value changethe bar scales along its length, retargeting mid-glide | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Indeterminate sweepa 40% band crosses the track, on a loop | duration.deliberateease.standard560ms · cubic-bezier(0.2, 0, 0, 1) · × 2 per sweep | 560mscubic-bezier(0.2, 0, 0, 1)× 2 per sweep |
Reduced motion
New values jump into place, and the indeterminate band becomes still stripes.
Ticks
| Moment | Tokens | ||
|---|---|---|---|
| Value changethe lit count follows the value; the edge tick lights partway and the next three warm up | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Indeterminate waveeach tick warms quickly and fades slowly, its turn later along the row, so a wave crosses in 70% of the loop and rests | duration.deliberateease.linear560ms · cubic-bezier(0, 0, 1, 1) · × 2 per loop | 560mscubic-bezier(0, 0, 1, 1)× 2 per loop |
Reduced motion
The lit count jumps to each value, and indeterminate shows every other tick lit, still.
The ticks only change opacity. They light from the inline start, so under right-to-left they fill and the wave travels from the right.
Bar
The bar stays full width and scales on its X axis, so only transform animates. A burst of updates retargets the spring with its velocity intact instead of restarting a tween, which is why the uneven upload chunks in the demo read as one steady glide. It fills from the inline start, so under right-to-left it fills from the right.
Reduced motion
The bar jumps to each new value instead of gliding, and the indeterminate sweep becomes still diagonal stripes across the track. The ticks light at once, and their indeterminate wave becomes every other tick lit, still. The amount done and the "still working" state both stay visible.
AccessibilityARIA, announcements
Accessibility
- It's a
progressbarwitharia-valuenowand a formattedaria-valuetext, named by itsProgressLabeloraria-label. - Indeterminate progress has no
aria-valuenow, which tells assistive technology the amount isn't known. - The ticks are decorative and hidden from assistive tech; the progressbar's value says the same thing.
- Screen readers don't reliably announce each change. When the work finishes, say so in a polite status, as "All files uploaded." does in the demo.