ARIA Rule

Progress bars must have an accessible name aria-progressbar-name

role="progressbar" tells a screen reader that something is in progress and how far along it is. It also needs a name saying what that something is: Uploading holiday-photos.zip, Step 2 of 4, Installing update. Without the name, the listener hears a percentage and nothing to attach it to, which is worse when several things are happening at once.

Websight reports this as ARIA progressbar nodes must have an accessible name

Who it affects

Someone using a screen reader hears progress bar, 62 percent and does not know if it is their upload, the page loading, or an advert.

Someone using a screen reader while two uploads run hears two unnamed bars and cannot tell which file is nearly done.

Someone using voice control cannot refer to the bar to ask for its value.

Broken versus fixed

Both examples are live and rendered in a sandboxed frame. The broken one is deliberately broken.

Broken

The file name is right above the bar. A screen reader announces: progress bar, 62 percent. The file name is separate text and is not part of the bar's announcement.

Fixed

Announced: Uploading holiday-photos.zip, progress bar, 62 percent. The visible text is the name, so there is one thing to keep up to date.

Show the markup behind both examples
Broken
<style>body{font-family:system-ui;padding:16px;margin:0;font-size:14px}.f{display:flex;justify-content:space-between;width:300px;margin-bottom:6px}.n{color:#2d3748}.p{color:#718096}.bar{width:300px;height:10px;border-radius:5px;background:#e2e8f0;overflow:hidden}.bar span{display:block;height:100%;width:62%;background:#2b6cb0}</style>
<div class="f"><span class="n">holiday-photos.zip</span><span class="p">62%</span></div>
<div class="bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="62"><span></span></div>
Fixed
<style>body{font-family:system-ui;padding:16px;margin:0;font-size:14px}.f{display:flex;justify-content:space-between;width:300px;margin-bottom:6px}.n{color:#2d3748}.p{color:#718096}.bar{width:300px;height:10px;border-radius:5px;background:#e2e8f0;overflow:hidden}.bar span{display:block;height:100%;width:62%;background:#2b6cb0}</style>
<div class="f"><span class="n" id="up">Uploading holiday-photos.zip</span><span class="p">62%</span></div>
<div class="bar" role="progressbar" aria-labelledby="up" aria-valuemin="0" aria-valuemax="100" aria-valuenow="62"><span></span></div>

Test it by hand in two minutes

  1. Inspect the element with role="progressbar". It needs aria-label, aria-labelledby pointing at visible text, or to be a native progress element with a label.
  2. In Chrome DevTools, select it and open the Accessibility pane. The computed Name should say what is progressing.
  3. Turn on VoiceOver (Cmd+F5) or NVDA and move to the bar. You should hear the name, then progress bar, then the value.
  4. If the bar is indeterminate (no aria-valuenow), check the name on its own tells the listener what to wait for.

The fix

Before
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="62"></div>
After
<div role="progressbar" aria-label="Uploading holiday-photos.zip" aria-valuemin="0" aria-valuemax="100" aria-valuenow="62"></div>

Say what is in progress. If there is text on screen that already says it, use aria-labelledby and point at that instead.

Native progress element

Before
<div role="progressbar" aria-valuenow="62" aria-valuemin="0" aria-valuemax="100"></div>
After
<label for="up">Uploading holiday-photos.zip</label>
<progress id="up" max="100" value="62">62%</progress>

The native element takes a label like any form control and reports its own value.

Multi-step form

Before
<div role="progressbar" aria-valuenow="2" aria-valuemin="0" aria-valuemax="4"></div>
After
<div role="progressbar" aria-label="Checkout progress" aria-valuenow="2" aria-valuemin="0" aria-valuemax="4" aria-valuetext="Step 2 of 4: Delivery"></div>

aria-valuetext turns the raw number into something a person would say.

Common mistakes

  • Naming it progress bar or loading, which repeats the role and says nothing about what is loading.
  • Putting the name on a wrapper rather than on the element with the role.
  • Hiding the visible file name with aria-hidden and then referencing it with aria-labelledby, which gives an empty name.
  • Forgetting to remove or update the bar when the task finishes, so it announces a stale percentage.
  • Using a progressbar for a static measurement like storage used; that is a meter.

WCAG criteria this rule maps to

axe-core tags this rule as WCAG 2.2 level A. It is one of the checks for:

The scanner can see a progressbar with an empty computed name. It cannot tell whether the name describes the right task, or whether the value is updated as the task runs. Automated tools find only part of the accessibility problems on a page; a pass on this rule is not a pass on the criterion.

Scan your own page for this and 63 other checks

Open any page in Chrome, click Websight, and get a score out of 100 with every issue explained and a draft fix snippet per element.

Add Websight to Chrome

Free scans, no account, nothing leaves your computer. Automated tools find only part of the problems; the extension says so on every report.