Skip to content

Progress

The class goes on a real <progress>. The element has the semantics built in: the browser reports it as role="progressbar" and announces how far it has got, so you write neither a role, aria-valuenow nor aria-valuemax. If you build the bar from <div> elements, you have to write all of that yourself, and keep it in step with the number.

If you do not know how far it has got, Spinner is the better fit.

The builder function always writes out max, contrary to the rule that a default value gives no attribute. That rule is about appearance, where the CSS already has the answer. max is semantics, and HTML’s own default is 1:

<progress value="40"></progress>

That bar is full, not 40 percent, and nothing tells you. If you write the markup by hand, give both.

If you leave out value, the bar becomes indeterminate, and a stripe sweeps across it. It says “something is in progress” without promising a number:

<progress class="lt-progress" aria-label="Fetching your applications"></progress>

That is the same thing Spinner says. Choose one of them in a given place, otherwise the page reports the same wait twice.

data-colorUsed for
(omitted)Something is in progress, and it is going as it should
successIt is finished
warningIt is approaching a limit
dangerIt stopped, or went wrong

The colour alone says nothing. Someone who cannot tell green from red needs the text next to it, and someone listening to the page only gets the aria-label. So write what happened, and let the colour confirm it.

ClassWhat it is
.lt-progressThe bar. The colour comes from data-color.
VariableDefault
--lt-progress-fillvar(--lt-color-accent-fill), and data-color changes it
--lt-progress-trackvar(--lt-color-neutral-surface)
--lt-progress-bordervar(--lt-color-neutral-border), the border around the track. Set it to transparent to remove it
--lt-progress-heightvar(--lt-spacing-2)
--lt-progress-radiusvar(--lt-spacing-1)

The fill colour is carried by color on the bar itself, and the filled part reads currentColor. The reason is that the filled part has two names, ::-webkit-progress-value in Chromium and WebKit and ::-moz-progress-bar in Gecko, and they cannot be in the same selector list: a pseudo-element the engine does not know makes the whole rule invalid. The colour therefore has to be decided in one place and inherited. <progress> has no text, so color is free for the job, and --lt-progress-fill is all you need to set to change it.

If the user has asked for reduced motion, the indeterminate stripe moves more slowly, and the filled part jumps to the new number instead of sliding there. The stripe does not stop completely: an indicator that stands still no longer says that something is in progress.

An example. A bar under a field should often be thinner and without corners:

See Customising for how the variables and the layer work.

  • Give the bar a name with aria-label, or tie it to a visible text with aria-labelledby. Without a name the screen reader reports a number without saying what it is about.
  • Always give max together with value. HTML’s default is 1, so a bar without max is full at anything above 1.
  • Do not update the number more often than the user needs. Every change can become an announcement, and a bar that counts from 0 to 100 in steps of one becomes a hundred messages.
  • When the progress is over, say so with more than the colour. Move focus to where the result appeared, or report it in a role="status" element.