Progress Bars

A Bootstrap 5 progress bar is a purely visual, purely-CSS way to show a value somewhere between empty and full — an upload's percent complete, a skill rating, or (as we'll build below) a TrailMap trail's difficulty meter. There's no bundled JavaScript at all: you set the width yourself, either inline or with Bootstrap 5.3's newer CSS custom property, and Bootstrap 5 supplies the striping, coloring, and animation on top of it.

1. The Basic .progress / .progress-bar Pair

The outer .progress element is the track (the full-width grey background); the inner .progress-bar is the filled portion, sized with an inline width style.

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

2. Why the ARIA Attributes Matter

A progress bar is visual by nature — a sighted user reads the fill level at a glance. A screen reader user cannot see that fill, so the three ARIA attributes are what actually communicate the value: role="progressbar" identifies the element's purpose, and aria-valuenow/aria-valuemin/aria-valuemax report the current value and its range as numbers an assistive technology can announce out loud. Skipping them leaves screen reader users with an announced element that has no reported value at all.

Note: aria-valuenow should always be kept in sync with the visual width — if you change the inline width with JavaScript for a live upload progress bar, update aria-valuenow in that same line of code.

3. Setting Width: Inline Style vs --bs-progress-width

The classic approach is an inline width style, shown above. Bootstrap 5.3 added a second option: the --bs-progress-width CSS custom property, which .progress-bar reads internally to size itself. Functionally the two are equivalent for a single bar — the custom property is mainly useful when you want to set the width from a CSS rule (a stylesheet or a <style> block) instead of an inline attribute, e.g. when the percentage comes from a CSS variable set elsewhere on the page.

<!-- Classic: inline width -->
<div class="progress-bar" style="width: 40%;" role="progressbar"
     aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>

<!-- Bootstrap 5.3+: the --bs-progress-width custom property -->
<div class="progress-bar" style="--bs-progress-width: 40%;" role="progressbar"
     aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>

4. Striped and Animated Variants

Add .progress-bar-striped for a diagonal-stripe texture across the fill, and .progress-bar-animated (used together with .progress-bar-striped) to make those stripes slide continuously — a common way to signal "still in progress" for something like an active file upload with an unknown finish time.

<!-- Striped -->
<div class="progress">
  <div class="progress-bar progress-bar-striped" role="progressbar"
       style="width: 75%;" aria-valuenow="75"
       aria-valuemin="0" aria-valuemax="100"></div>
</div>

<!-- Striped AND animated (moving stripes) -->
<div class="progress">
  <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
       style="width: 45%;" aria-valuenow="45"
       aria-valuemin="0" aria-valuemax="100"></div>
</div>
Tip: Pair a color utility like .bg-success, .bg-warning, or .bg-danger on the .progress-bar itself with striping/animation to signal both a state AND an in-progress action at once.

5. Worked Example — TrailMap Difficulty Meter

A trail difficulty meter for Eagle Peak Trail, shown as a colored progress bar out of 5 difficulty points, plus a genuine upload-progress bar for the "Submit a Trail" form's photo upload step.

<!-- Difficulty meter: Eagle Peak Trail rated 4 out of 5 -->
<label class="form-label">Difficulty: Hard (4/5)</label>
<div class="progress" style="height: 10px;">
  <div class="progress-bar bg-danger" role="progressbar"
       style="width: 80%;"
       aria-valuenow="4" aria-valuemin="0" aria-valuemax="5"></div>
</div>

<!-- Photo upload progress on the "Submit a Trail" form -->
<label class="form-label">Uploading trail-photo.jpg…</label>
<div class="progress">
  <div class="progress-bar progress-bar-striped progress-bar-animated bg-info"
       role="progressbar" style="width: 58%;"
       aria-valuenow="58" aria-valuemin="0" aria-valuemax="100">
    58%
  </div>
</div>

Note the difficulty meter uses aria-valuemax="5" instead of the usual 100 — the ARIA attributes don't require a percentage scale, only that aria-valuenow sits between aria-valuemin and aria-valuemax and that all three describe the same unit consistently. The upload bar, being an actual in-progress action, adds the striped + animated combo so it visually reads as "still working," not a static rating.

Next up: Modal — our first component in the "Interactive (JS)" group, where Bootstrap 5's vanilla-JS bundle drives a real show/hide lifecycle instead of pure CSS.