Progressbar

Description

A progress bar is a graphical representation of the completion status of a task or process. Unlike a meter, which represents a static scalar measurement (like disk usage), a progress bar indicates progress for a process or task currently underway.

  • Progress bars are used to display how much of a defined task has been completed.
  • They provide a visual and programmatic indication of current status relative to a maximum value.

When to use

  • Use a progress bar to show the status of a process, such as a file upload or a multi-step form completion.
  • Avoid using a progress bar for static measurements or gauges; use a meter instead.
  • Progress bars should include visual or programmatic labels so users understand the context of the progress being shown.

Native HTML Progress Bar

Examples

  • HTML <progress> element: The standard element for representing the completion progress of a task.
1<label for="file-upload">Uploading file:</label>
2
3<progress id="file-upload" value="70" max="100">70%</progress>

Additional Attributes

The state of a progress bar is defined using native attributes:

  • value: The current numerical progress.
  • max: The upper bound of the range (defaults to 1).
  • If the value attribute is omitted, the progress bar is considered "indeterminate," indicating that progress is occurring but it is unknown how much time the process will take.

Semantics and Structure of a Custom Progress Bar

A custom progress bar is a read-only widget that conveys a process's completion status without using the native HTML <progress> element. It’s construction consists of one or more elements, one of which must have a role attribute with the value of progressbar.

HTML elements that can accept the progresbar role include <img>, <svg>, <div>, <span> and other elements that can accept any role.

Descendants of the element with role=progressbar, if any, are presentational only and are not exposed to assistive technologies.

The following example illustrates one of the many ways to construct an accessible progress bar. JSX syntax is used here for the variable compressed.

1<div id="compress-label">Compression progress</div>
2<div
3 class="progressbar"
4 role="progressbar"
5 aria-labelledby="compress-label"
6 aria-valuenow={compressed}
7 aria-valuetext={compressed + " percent compressed">
8 <div
9 class="progressbar__progress"
10 style={`width: ${compressed}%`}>
11 </div>
12</div>
13<div aria-hidden="true">{compressed}% compressed</div>
14<style>
15.progressbar {
16 position: relative;
17 width: 100%;
18 border: 2px solid gray;
19}
20.progressbar__progress {
21 /* set border rather than background for high contrast visibility */
22 height: 0;
23 border-bottom: 8px solid gray;
24}
25</style>

ARIA

For custom progress bar components, ARIA attributes are required to convey the element's role and state to assistive technologies:

  • Role: The component must have role="progressbar" to identify it as a progress indicator.
  • Value Attributes are optional:
    • aria-valuenow: The current completion value (indeterminate if not set).
    • aria-valuemin: The minimum value of the range (0 if not set).
    • aria-valuemax: The maximum value of the range (100 if not set).
  • Value Text: Use aria-valuetext to provide a human-readable string representation of the progress (e.g., "Step 2 of 4") if the numerical value alone is insufficient.
  • Labeling: Ensure the progress bar has an accessible name. If the progress bar has a visible label, set aria-labelledby to the id of the label, otherwise set aria-label.

Relationships

A progress bar must be programmatically associated with its label so users of assistive technology understand what task is in progress. When a text label is presented, the accessibility label should be set through a relationship between the text label and the progress bar using one of the following methods:

  • For an HTML <progress> element, use the <label> element with a for attribute matching the progress bar's id to wrap the visible label.
  • On either a <progress> element or a custom progress bar (with role="progressbar"), set the attribute aria-labelledby with the id of the visible label.

If a visible label is not provided, provide an appropriate label with the aria-label attribute on either a native or custom progress bar element.

Operation

Pointer operation

A user should be able to see the visual state of the progress bar change as the underlying value changes. While progress bars are typically read-only, if they are part of an interactive dashboard, clicking them should not trigger unexpected actions unless clearly labeled.

Keyboard operation

  • Focus: Progress bars are typically not focusable unless they are interactive or provide additional information on hover/focus. If focusable, they must be reachable via the Tab key.

  • Announcement: When a progress bar receives focus (if focusable), a screen reader should announce its label and its current value/range.

Tests

Element has incorrect role

FieldValue
ImpactCritical
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

The ARIA role attribute must be explicitly set to progressbar for all custom progress bar elements to ensure assistive technologies correctly identify the component.

Missing contextual labeling

FieldValue
ImpactModerate
WCAG1.3.1 Info and Relationships
EN 301 5499.1.3.1 Info and Relationships
Section 508N/A

A progress bar without a label is a "silent" graphic. Ensure every progress bar has a clear accessible name so users know what is being tracked (for example, “Export progress”).

Missing range attributes

FieldValue
ImpactCritical
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Unless the task status is truly indeterminate, custom progress bars should set a value for aria-valuenow between aria-valuemin and aria-valuemax and native <progress> elements should set its value attribute less than its max value.

In most cases, tasks begin with 0 progress, and aria-valuemin need not be set. (The <progress> element does not have a min attribute—its minimum value is always 0.)

For a custom progress bar, a value must be set for aria-valuemax if the maximum value is not 100. For the <progress> element, a value must be set for the max attribute when the maximum value is not 100.

If no value is set, most screen readers will announce the status as indeterminate. For clarity, best practice is to also set an appropriate label or supplementary text referenced with aria-describedby. (For example, “Loading . . .”)

Avoid using aria range attributes on a native progress bar as they may conflict with the implicit default values of the progress bar and create a confusing experience for screen reader users.

Range Logic Errors

FieldValue
ImpactCritical
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 508N/A

Several critical errors can occur if the numerical range is defined incorrectly:

  • Min value is greater than the max value: The aria-valuemin must be less than aria-valuemax.
  • Range has equal min and max values: A range requires a difference between the minimum and maximum to be valid.
  • Value out of range: The value must fall within the bounds of min and max.

Meaningful aria-valuetext

FieldValue
ImpactSerious
WCAGN/A
EN 301 549N/A
Section 508N/A

Assistive technologies often present aria-valuenow as a percentage. If conveying the progress only in terms of a percentage would not be user friendly or sufficient, the aria-valuetext property must be set to a string that makes the progress value understandable. For example, a file upload progress bar might convey its value as aria-valuetext="Downloading file (3 of 10) - 50% complete".

Note: For progress bars, aria-valuetext should only be used to supplement the numerical value. The numerical aria-valuenow attribute must always be included for custom progress bars unless the status is truly indeterminate.

Nested interactive elements

FieldValue
ImpactSerious
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Avoid nesting buttons or links inside the progress bar container. This disrupts the focus order, and screen readers ignore all nested elements and may ignore the progress bar itself.

Avoid aria-owns

Do not use aria-owns to define a progress bar relationship. While aria-owns can define a parent-child relationship between elements that are not nested in the DOM, it is often poorly supported by assistive technologies and can lead to a confusing reading order. It is best to use standard DOM nesting.

Component does not support disabled state

FieldValue
ImpactSerious
WCAG4.1.2 Name, Role, Value
EN 301 5499.4.1.2 Name, Role, Value
Section 5081194.21(d)

Progress bars are typically read-only status indicators and do not inherently support a disabled state like interactive controls do. The disabled attribute should not be applied to the native <progress> element or a custom component with role="progressbar". If a visual indication of being inactive is necessary, use standard styling and ensure no inappropriate ARIA state is communicated.