( Perceivable / WCAG 1.1.1 )

ARIA progressbar has no accessible name

SeriousLevel AWCAG 1.1.1 — Non-text Content

What is this issue?

An element carrying role="progressbar" has no accessible name: the text a screen reader announces to identify an element. It comes from the first source that applies, in order: aria-labelledby, aria-label, visible label text, then other fallbacks specific to the element type.

The progressbar role represents an operation moving toward completion (a file upload, a page load, a multi-step wizard) and exposes its position through aria-valuenow, aria-valuemin, and aria-valuemax (or omits them entirely for an indeterminate operation with no known duration). Without an accessible name, the percentage or state is announced, but what’s actually completing is not.

Why does this matter?

A progress bar’s whole point is to tell the user something is happening and roughly when it’ll finish. A number with no name doesn’t do that: “60 percent” gives no indication whether a video is buffering, a file is uploading, or the page itself is still loading content the user is waiting on.

This becomes a real problem the moment more than one progress bar can appear at once, which is common on file-upload interfaces handling multiple files or a page with several async operations running in parallel. A sighted user tells them apart by the filename or label printed above each bar. A screen reader user hearing “60 percent, progress bar” three times with no names has no way to know which operation is which, or whether any of them has stalled.

Who is affected?

  • Screen reader users: hear a percentage and a role with no indication of what’s completing, and can’t distinguish between multiple concurrent progress indicators on the same page.
  • Cognitive disabilities: users tracking a multi-step process (an upload, an import, a checkout flow) lose the confirmation a named progress bar would give that the right operation is proceeding, adding uncertainty about whether to wait or retry.

What users experience

Wale uses NVDA on Windows to upload three photos to a shared album at once. Each photo gets its own progress bar, all built as <div role="progressbar"> with no aria-label. NVDA announces “34 percent, progress bar” for the first one he tabs to, with nothing distinguishing it from the other two. He can’t confirm which photo is uploading fastest or whether one has stalled, so he waits for all three to visually disappear from the page before trusting the upload finished.

How do I fix it?

Add aria-label naming the operation the progress bar tracks, such as aria-label="Uploading vacation-photo.jpg". This works because it gives the screen reader a specific subject for the percentage, turning a bare number into a complete, understandable status.

When the progress bar sits next to visible text describing the operation (a filename, a step label in a wizard), use aria-labelledby pointing at that element’s id instead. This keeps one source of truth for the name instead of risking the visible caption and the aria-label drifting apart if either is edited later.

For an indeterminate operation with no known completion percentage, omit aria-valuenow entirely rather than guessing a fake value; the role still announces correctly as “busy” or indeterminate, and the accessible name requirement is unchanged either way.

Code Examples

Before
<div role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100"></div>
After
<!-- Method 1: aria-label naming the specific operation -->
<div
  role="progressbar"
  aria-label="Uploading vacation-photo.jpg"
  aria-valuenow="45"
  aria-valuemin="0"
  aria-valuemax="100"
></div>

<!-- Method 2: aria-labelledby reusing a visible filename caption -->
<p id="upload-name">vacation-photo.jpg</p>
<div
  role="progressbar"
  aria-labelledby="upload-name"
  aria-valuenow="45"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuetext="45% uploaded"
></div>

Both methods turn “45 percent, progress bar” into a specific status naming the file. Method 2 also adds aria-valuetext, phrasing the value as “45% uploaded” instead of a bare number, which reads more naturally once the operation itself has a name attached.

Common Mistakes

Mistake: “The progress bar is inside a card that already has a filename heading, so it’s labelled.” A heading elsewhere in the DOM contributes nothing to the accessible name unless it’s connected with aria-labelledby. Visual containment inside the same card is not a programmatic relationship a screen reader can use.

Mistake: “Live-announcing every percentage change makes this more accessible.” Wiring an aria-live region to announce every single percentage tick produces a stream of interruptions a screen reader user can’t act on and can’t turn off mid-operation. Update aria-valuenow silently as progress changes, and reserve live announcements for meaningful milestones like completion or failure.

Mistake: “A spinning icon with no percentage doesn’t need this rule.” If the spinner carries role="progressbar" with no aria-valuenow (a valid, indeterminate progress bar), it still needs an accessible name explaining what’s in progress; the missing value doesn’t exempt it from needing a name any more than a determinate one is exempt.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s aria-progressbar-name rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule selects every element with role="progressbar" and checks whether the accessible name computation resolves to non-empty text.

False negative: axe-core confirms a name exists but can’t judge whether it’s accurate. A progress bar labelled aria-label="Loading" on every operation across a page (upload, import, and page load alike) passes automatically even though it doesn’t distinguish between them. False positive: none typical for this check, since resolving to a non-empty accessible name is a binary condition. Manual step: when more than one progress bar can appear simultaneously, confirm each announces a name specific enough to tell them apart.

Manual Testing

  1. Open the page in Chrome or Firefox with NVDA or JAWS running.
  2. Trigger an operation that shows a progress bar: a file upload, an import, or a multi-step process.
  3. Tab to the progress bar and listen to what’s announced. It should name the operation (“Uploading vacation-photo.jpg, 45 percent”), not just the percentage and role.
  4. If more than one progress bar can appear at once, confirm each has a distinct, specific name rather than all sharing an identical generic label.
  5. Repeat with VoiceOver on a Mac to confirm the announcement is consistent, since dynamic value updates can behave slightly differently across screen readers.

1.1.1 Non-text Content: Non-text content must have a text alternative that serves the equivalent purpose. A progress bar’s visual fill conveys status graphically; its accessible name and value together are the text alternative, and a missing name breaks that equivalence for screen reader users.

4.1.2 Name, Role, Value: Every interface component must expose a name, role, and current value to assistive technology. A progress bar with no accessible name has a role and, usually, a value, but no name, which is this criterion’s requirement failing alongside 1.1.1.

ARIA meter has no accessible name is the closest sibling rule: the identical missing-name failure on the role for a static or slowly-changing gauge rather than an operation moving toward completion.

ARIA command has no accessible name covers the same underlying failure on interactive controls, which a progress bar sometimes appears alongside, such as a cancel button next to an upload’s progress bar.

ARIA attribute is required but missing is worth checking on the same element, since a valid progressbar role also depends on correctly formed aria-valuemin/aria-valuemax pairs when a determinate value is used.

ARIA input field has no accessible name shares the same aria-label/aria-labelledby fix pattern, applied to data-entry widgets instead of status displays.

References

Frequently asked questions

Does a progressbar need aria-valuenow to be valid, or can it be indeterminate?

A progressbar can omit aria-valuenow to represent an indeterminate operation with no known completion percentage, such as a spinner with unknown duration. Either way, the accessible name is still required, since the name describes what is happening, not how far along it is.

Should I announce every percentage change through the progressbar?

No. Constantly updating live-region announcements for every percentage tick creates a stream of noise a screen reader user cannot act on. Update aria-valuenow silently as progress changes, and reserve an announcement for meaningful milestones or completion, typically through a separate aria-live region.

Does a spinner with no percentage still count as a progressbar for this rule?

Only if it carries role="progressbar". A purely decorative CSS spinner with no ARIA role at all is not covered by this rule, though it should usually have its own accessible name or be hidden with aria-hidden so it does not announce as an unlabelled generic element.

Is aria-valuetext required alongside the accessible name?

No, aria-valuetext is optional. It replaces how the current value is announced, for example turning "45" into "45 percent, 2 of 4 files uploaded," which is a readability improvement on top of the name, not a substitute for it.

Does this rule apply to native HTML progress elements?

The native progress element resolves its accessible name through the same sources as the ARIA role, so an unlabelled progress element has an equivalent problem, though it typically surfaces under a different automated check than this ARIA-specific rule.