Progress

progress.progress

Progress — how far along a task is.

Examples

A task, with the number in text

View “A task, with the number in text” as

Uploading photos — 30%

<p id="upload-status" aria-live="polite">Uploading photos — 30%</p>
<progress class="progress" aria-labelledby="upload-status" max="100" value="30"></progress>

The sentence above the bar does three jobs at once: it names the bar, it shows the number the bar cannot, and it is the live region that announces the change.

<progress> is not a live region and a bar that fills silently tells a screen reader user nothing.

Indeterminate

View “Indeterminate” as

Preparing your export…

<p id="export-status" aria-live="polite">Preparing your export…</p>
<progress class="progress" aria-labelledby="export-status"></progress>

No value attribute at all — that is what makes it indeterminate. Under prefers-reduced-motion the animation stops outright rather than speeding up: an infinite 0.01ms animation is a busy loop, not a stilled one.

Tokens 7

Level 2, declared on .progress itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by progress
TokenDefault
--progress-block-size1rem
--progress-indeterminate-durationvar(--duration-slower)
--progress-radiusvar(--radius-full)
--progress-track-border-colorvar(--color-border)
--progress-track-colorvar(--color-surface-sunken)
--progress-transition-durationvar(--duration-normal)
--progress-value-colorvar(--color-primary)

Variants and states

Variants

  • .progress-danger
  • .progress-lg
  • .progress-sm
  • .progress-success
  • .progress-warning

State it reads

None.

Before you ship it

What you have to do 1 requirements

  1. Give it a name. <progress> is a labelable element, so <label for> does compute a name in current engines, but a progress bar is rarely next to a form label — aria-labelledby pointing at the heading or sentence that already describes the task is usually the honest markup, and aria-label is the fallback when no such text exists. An unnamed progress bar is announced as a bare percentage. <progress> is not a live region. The bar filling from 10% to 90% is silent: nothing is announced unless the user's cursor happens to be on it. For a long-running task, put a sentence next to the bar and mark that aria-live="polite" — "Uploading, 30%" — and update it in coarse steps (every 10%, or on state changes) rather than on every frame, which floods the speech queue. When the task ends, say so in the same region; a bar that reaches 100% announces nothing either. The percentage is not visible. This component paints a bar, not a number. If the figure matters, write it in your own markup next to the bar. A CSS-generated number would be unselectable and, on <progress>, unrenderable — ::before/::after do not apply to a replaced element.

src/css/components/progress.css · npx mostlycss add progress