Components / feedback

Progress

Sharing Meter's anatomy was deliberate: two bars that look different for no reason is the drift a shared stylesheet prevents.

Rendered from the package · open on its own

Summary

How much of a job is done.

APG patternnone
Built onnative elements
Statusstable
Since0.2.0
<Progress label="Uploading" value={3} max={10} valueText="3 of 10 images" />
<Progress label="Uploading" indeterminate />

Anatomy

The same anatomy as Meter, so one stylesheet dresses both shapes while the two stay apart semantically.

PartElementRequired
fill<div>yes
header<div>no
label<span>no
track<div>yes
value<span>no

track carries role="progressbar" and the aria-value* set. fill is sized inline.

Properties

PropertyValuesDefaultWhat it does
valuenumbernoneIgnored when indeterminate is set.
minnumber0The bottom of the range.
maxnumber100The top of the range.
labelReact.ReactNodenoneDrawn beside the bar and used as the control's accessible name.
valueTextstringnoneOverrides the percentage shown and announced — "3 of 10", "2.1 MB of 8 MB".
indeterminatebooleannoneThe work has no countable steps yet. Drops aria-valuenow, which is what ARIA means by indeterminate, and cycles the bar rather than filling it.
idstringnoneWires the label to the bar. One is generated when you omit it, so the accessible name cannot be lost by forgetting.

States

StateExpressed as
Mixeddata-indeterminate

Behavior

The fill grows. It never shrinks — if the number can go down, this is the wrong component.

Indeterminate cycles a third of the track across instead of filling.

Accessibility

Indeterminate drops aria-valuenow, which is what the ARIA spec means by indeterminate. It is a state, not a missing value.

The label is the accessible name. Passing none leaves a bar a screen reader can find and cannot describe.

valueText is announced as well as drawn, so the two cannot disagree.

Keyboard

None of its own. It is not focusable and takes no keys.

Roles, states and properties

AttributeSet by
aria-valuemaxset at render
aria-valueminset at render
rolealways progressbar

Focus

Never takes focus.

Screen reader

Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.

Contrast and target size

npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.

It sets no target size of its own; anything interactive inside it brings its own.

Tested

  • test/browser/feedback.spec.mjs

Known gaps

_No upstream requirement is recorded as not applicable._

Appearance

TokenUsed for
--se-accentbackground
--se-duration-mediumtransition
--se-duration-slowanimation
--se-ease-standardanimation, transition
--se-font-codefont-family
--se-radius-pillborder-radius
--se-space-2block-size, gap
--se-space-3gap
--se-surface-sunkbackground
--se-textcolor
--se-text-mutedcolor
--se-text-smfont-size

Motion

The fill crosses --se-duration-medium when the value changes. Indeterminate cycles over three times --se-duration-slow, forever, and the reduced-motion reset stops it after one pass.

Content

valueText is for anything a percentage says badly. "3 of 10 images" is what a reader wants; "30%" is what the arithmetic produced. Use the unit the reader thinks in — images, megabytes, steps.

Usage

Not a Meter. A meter reports a level that can fall again — disk used, budget spent.

Not a Spinner. The moment you can count the steps, counting them is the kinder answer.

Indeterminate is not a spinner in a different shape. Use it where a bar's shape belongs but the total is not known yet — an upload whose size the server has not reported.

Notes

Sharing Meter's anatomy was deliberate: two bars that look different for no reason is the drift a shared stylesheet prevents.