Code examples

Progress bar

There are many variations of progress bars and loading spinners, some of which may not need to be a true progress bar at all.

Support varies by screen reader. It’s recommended to add full aria attributes, even when using a native <progress> element.

Use semantic HTML

  • This semantic HTML contains all accessibility features by default.
  • While not a requirement, it is focusable to increase discoverability.
<progress role="progressbar"
          id="progress"
          tabindex="0"
          class="progress"
          aria-label="File upload"
          value="50"
          aria-valuemin="0"
          aria-valuenow="50"
          aria-valuemax="100"
          max="100">

Spinner loading takeover

  • There are many variations of loaders / spinners.
  • While a takeover spinner modal is present, other content on the page must be inert

Use semantic HTML

  • This semantic HTML contains all accessibility features using a dialog.
    • The progress element can be used to describe the state

Ensure content is ready before being available

  • If content is being loaded slowly behind the spinner inside an aria-live region, use aria-busy="true" to keep it from being read until the update is complete
<!-- Use aria-busy if content doesn't all load at once -->
<div id="really-slow-app" 
     aria-live="polite" 
     aria-busy="false">
     
  <button id="showModal">
    Launch spinner
  </button>

  <dialog role="dialog"
          class="takeover"
          id="modal"
          tabindex="-1"
          aria-modal="true"
          aria-labelledby="modal-title">
    <section>
      <div class="progress-spinner">
        <progress role="progressbar" 
                  id="modal-title" 
                  tabindex="0" 
                  aria-label="Loading">
      </div>
    </section>
  </dialog>
</div>

Inline dynamic loading waiting example

This example dynamically injects progress updates that will be read by a screen reader

  • aria-busy="true" has spotty support, but does indicate that the region is busy
  • aria-describedby is allows the current progress to be read when the button is focused
  • aria-disabled reinforces that the save action is incomplete
  • role="status has an implicit aria-live=”polite” of polite and aria-atomic="true" meaning the entire content of the status will be read on each update
<div 
  id="slow-app"
  aria-live="polite">
  
  <button 
    id="trigger-progressbar"
    aria-describedby="progress-busy"
    aria-disabled="false">
      Save
  </button>

  <div class="progress-busy inert" role="status">
    <span id="progress-busy">
    </span> 
  </div>
</div>

Developer notes

Name

  • Use aria-label="Progress bar name" when there is not a visible title.

Role

  • Use role="progressbar

Group

  • If the progress bar is describing another region of the page, use aria-describedby="progressbar-id" to connect the two elements.

State

  • The state will be read out to the screen reader user by default.

Focus

  • Progress bar is not usually focusable.

Reference