ThisCalculator

Everyday

Bootstrap 5 Progress Bars: Accessible Markup?

Updated · Standard formula, shown below

The calculator for this page is switched off while it is checked. The formula is below, and the worked example in the article shows how to use it by hand.

Bootstrap 5 Progress Bars: Accessible Markup?

Yes, Bootstrap 5’s progress bar component includes the correct accessible markup out of the box, provided you add the required role="progressbar", aria-valuenow, aria-valuemin, and aria-valuemax attributes. The default Bootstrap 5 example in the documentation already contains these ARIA attributes, so copying that exact structure gives you a progress bar that screen readers can interpret correctly. However, there are common mistakes—like omitting the visually hidden text or using an incorrect aria-valuenow value—that can break accessibility in practice. This article explains the markup, why each attribute matters, and how to avoid the pitfalls that trip up many developers.

Formula

The accessible progress bar markup uses a simple formula to determine the visual width and the aria-valuenow value:

  • Visual width = (value – min) / (max – min) × 100%
  • aria-valuenow = the current value (as a number)

Variables:

  • value: the current progress (e.g., 50)
  • min: the minimum value (usually 0)
  • max: the maximum value (usually 100)

In Bootstrap, the .progress-bar element gets a style="width: X%" and the same numeric value is placed in aria-valuenow. If your progress is not on a 0–100 scale, you still set aria-valuemin and aria-valuemax to the actual bounds, and the browser calculates the percentage for assistive tech.

Worked Example

Using the default values from the calculator (value = 50, min = 0, max = 100):

  1. Visual width = (50 – 0) / (100 – 0) × 100 = 50%
  2. aria-valuenow = 50
  3. aria-valuemin = 0
  4. aria-valuemax = 100

The resulting Bootstrap markup:

<div class="progress" role="progressbar" aria-label="Example" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
  <div class="progress-bar" style="width: 50%"></div>
</div>

This is exactly what Bootstrap 5 documentation shows, and it works correctly with screen readers.

Why ARIA Attributes Matter

Native HTML has no built-in progress bar element that conveys a percentage to assistive technology. The <progress> element exists, but Bootstrap chose to use a <div> with ARIA roles for easier styling and cross-browser consistency. ARIA (Accessible Rich Internet Applications) fills the gap by telling screen readers:

  • role=”progressbar”: identifies the element as a progress indicator
  • aria-valuenow: the current value (required)
  • aria-valuemin: the minimum value (required)
  • aria-valuemax: the maximum value (required)
  • aria-label or aria-labelledby: provides an accessible name (recommended)

Without these, a screen reader simply reads the text content (if any) or nothing at all, leaving users confused about the progress state.

Common Mistakes and How to Avoid Them

Even experienced developers make these errors:

  • Omitting aria-valuenow: If you leave it out, some screen readers announce the progress bar as indeterminate, which is wrong for a determinate bar.
  • Using text inside the bar instead of aria-label: If you put “50%” as text inside the bar, screen readers may read it, but they won’t know it’s a progress bar unless the role and values are present. Always add an aria-label.
  • Setting aria-valuenow to a string: It must be a number, not “50%”.
  • Forgetting to update aria-valuenow when dynamically changing the width: If you use JavaScript to change the width, you must also update aria-valuenow. Otherwise, the visual and accessible values diverge.
  • Using role=”progressbar” on the outer div in Bootstrap 5: Bootstrap 5 moved the role to the outer .progress div, not the inner bar. This is a change from Bootstrap 4, so be careful when copying old code.

To avoid these, always copy the exact markup from the Bootstrap 5 documentation and test with a screen reader (like NVDA or VoiceOver) to verify.

Comparing Bootstrap 5 to Native HTML

Bootstrap’s approach differs from the native <progress> element:

  • <progress>: Built-in semantics, but styling is limited and inconsistent across browsers. You can’t easily add stripes or animations without extra CSS.
  • Bootstrap .progress: Full styling control, but requires ARIA attributes to be accessible. The component is also more flexible (e.g., you can set min/max to any values, not just 0–100).

If you don’t need Bootstrap’s styling, the native <progress> is simpler and automatically accessible. But if you’re already using Bootstrap, the ARIA pattern is well-established and works reliably when implemented correctly.

How to Test Accessibility

Testing is straightforward:

  1. Open the page in a browser with a screen reader (NVDA on Windows, VoiceOver on Mac).
  2. Navigate to the progress bar and listen to the announcement. It should read something like “Progress bar, 50%” or “50% progress”.
  3. Use browser dev tools to inspect the element and ensure all ARIA attributes are present and correct.
  4. If the bar updates dynamically, verify that aria-valuenow changes in the DOM.

Automated tools (like axe or Lighthouse) can catch missing attributes, but they can’t verify the actual screen reader experience, so manual testing is essential.

Frequently Asked Questions

Do I need to add role=”progressbar” to the inner div or the outer div?

In Bootstrap 5, the role and ARIA attributes go on the outer .progress div. The inner .progress-bar only handles the visual width. This changed from Bootstrap 4, where the role was on the inner bar.

What if my progress bar has a label like “Uploading…”?

Use aria-labelledby to point to the label’s ID, or use aria-label directly. For example: <div class="progress" role="progressbar" aria-labelledby="upload-label" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100">.

Can I use a progress bar without a visible percentage text?

Yes, but you should still provide an accessible name via aria-label so screen reader users know what the bar represents. The visual bar can be empty or have a striped animation.

Is the <progress> element better than Bootstrap’s ARIA approach?

For pure accessibility, <progress> is simpler and has native semantics. However, Bootstrap’s approach is equally accessible when the ARIA attributes are correctly set, and it offers more styling flexibility. Choose based on your project’s needs.

How do I make a progress bar accessible when it’s indeterminate (no exact value)?

Bootstrap doesn’t have a built-in indeterminate state. You can omit aria-valuenow and use a CSS animation, but you must also remove role="progressbar" and instead use role="progressbar" with aria-valuetext="indeterminate"? Actually, the correct approach is to use aria-valuenow with a placeholder? No—the ARIA spec says if aria-valuenow is absent, it’s indeterminate. So you can omit it and add a CSS animation. Test with a screen reader to confirm it announces as indeterminate.

Related

Formula used
in t | Progress value | % | 50 | 0
in t | Minimum value | - | 0 | 0
in t | Maximum value | - | 100 | 1
out aria | aria-valuenow | - | t | 0
out width | Visual width | % | (t - min) / (max - min) * 100 | 1

Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.