Progress bar

Use a progress bar for determinate progress towards a goal: trainings this week, applicants checked, steps of an import. It always comes with a visible label above and a value text below — the bar alone says nothing exact.

Rendered as the native <progress> element, labelled by the visible label via aria-labelledby. The value text defaults to "{value} von {max}"; pass text for units ("1 von 3 Trainings", "CHF 340 von 500").

For indeterminate waiting use a spinner or skeleton; for sets of an exercise use Progress segments; for one percentage in a compact tile use the Progress ring.

Examples

Weekly plan

Plan diese Woche1 von 3 Trainings1 von 3 Trainings
Data
render({
  "id": "plan-week",
  "label": "Plan diese Woche",
  "value": 1,
  "max": 3,
  "text": "1 von 3 Trainings"
})
Markup
<div class="field"><span class="field__label" id="plan-week-label">Plan diese Woche</span><progress class="progress" id="plan-week" max="3" value="1" aria-labelledby="plan-week-label">1 von 3 Trainings</progress><span class="u-subtle">1 von 3 Trainings</span></div>

Admin task

Bewerbungen geprüft31 von 3831 von 38
Data
render({
  "id": "applicants-checked",
  "label": "Bewerbungen geprüft",
  "value": 31,
  "max": 38
})
Markup
<div class="field"><span class="field__label" id="applicants-checked-label">Bewerbungen geprüft</span><progress class="progress" id="applicants-checked" max="38" value="31" aria-labelledby="applicants-checked-label">31 von 38</progress><span class="u-subtle">31 von 38</span></div>

Label hidden

KI-Budget Oktober1680 von 2000 Credits verbraucht1680 von 2000 Credits verbraucht
Data
render({
  "id": "budget",
  "label": "KI-Budget Oktober",
  "hideLabel": true,
  "value": 1680,
  "max": 2000,
  "text": "1680 von 2000 Credits verbraucht"
})
Markup
<div class="field"><span class="field__label u-visually-hidden" id="budget-label">KI-Budget Oktober</span><progress class="progress" id="budget" max="2000" value="1680" aria-labelledby="budget-label">1680 von 2000 Credits verbraucht</progress><span class="u-subtle">1680 von 2000 Credits verbraucht</span></div>

Data contract

What render(data) accepts — validated against this JSON Schema in development and tests. Unknown fields are rejected. The same schema is in components.json.

FieldTypeRequiredDefaultDescription
idstringrequired—Id prefix; the label gets {id}-label and the bar {id}. Must be unique on the page.
pattern ^[A-Za-z][\w-]*$
labelstringrequired—Visible label above the bar ("Plan diese Woche").
min 1 chars
hideLabelbooleanoptionalfalseHide the label visually (kept for screen readers) when a heading right above already names it.
valuenumberrequired—Current value (≤ max).
≥ 0
maxnumberrequired—Goal value.
≥ 1
textstringoptional—Visible value text under the bar; default "{value} von {max}". Include the unit ("1 von 3 Trainings").
JSON Schema
{
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "label",
    "value",
    "max"
  ],
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[A-Za-z][\\w-]*$",
      "description": "Id prefix; the label gets `{id}-label` and the bar `{id}`. Must be unique on the page."
    },
    "label": {
      "type": "string",
      "minLength": 1,
      "description": "Visible label above the bar (\"Plan diese Woche\")."
    },
    "hideLabel": {
      "type": "boolean",
      "default": false,
      "description": "Hide the label visually (kept for screen readers) when a heading right above already names it."
    },
    "value": {
      "type": "number",
      "minimum": 0,
      "description": "Current value (≤ `max`)."
    },
    "max": {
      "type": "number",
      "minimum": 1,
      "description": "Goal value."
    },
    "text": {
      "type": "string",
      "description": "Visible value text under the bar; default \"{value} von {max}\". Include the unit (\"1 von 3 Trainings\")."
    }
  }
}

Markup & states

Root: progress (inside div.field).progress — usable without render() by writing the markup directly.

Attributes

AttributeValuesDefaultDescription
value / maxnumbers—Native progress values.
aria-labelledby"{id}-label"—Links the bar to its visible label.

Parts

PartDescription
.fieldVertical wrapper (label, bar, text).
.field__labelThe label (span, id {id}-label).
.u-subtleVisible value text.

Accessibility

  • Native <progress> exposes value and max; the visible label names it via aria-labelledby.
  • The value is also visible as text, so it never depends on reading the bar length or colour.
  • Fallback content inside <progress> repeats the value text for very old browsers.

Guidance

DoShow progress towards the person's own goal (UX guide §13).
DoUse a status fill only when it means status (over budget) — and say it in the text.
Don'tDon't use a progress bar for unknown durations.
Don'tDon't rank minors with bars against each other.

Related: Progress segments · Progress ring · Stepper · Action tile