Range & Switches

Two more control types round out the Bootstrap 5 forms toolkit: .form-range, which restyles a native <input type="range"> slider, and .form-switch, which turns a plain checkbox into an iOS-style toggle. Both are still native HTML inputs under the styling, which matters for accessibility. This lesson builds a TrailMap "max distance" slider and a "parking available" switch.

Range Sliders: .form-range

Add .form-range to a native <input type="range">. It's still a plain range input — min, max, step, and value all work exactly as they do without Bootstrap; the class only changes how the track and thumb look.

<!-- TrailMap: max distance filter, 0-25 miles -->
<label for="maxDistance" class="form-label">Max Distance: 10 mi</label>
<input type="range" class="form-range" id="maxDistance" min="0" max="25" step="1" value="10">

Toggle Switches: .form-switch

A switch is a checkbox with two extra classes: .form-switch on the wrapping .form-check div, and role="switch" is implied by that combination — Bootstrap draws it as a pill-shaped track with a sliding circle instead of a square checkbox.

<!-- TrailMap: only show trails with parking -->
<div class="form-check form-switch">
  <input class="form-check-input" type="checkbox" role="switch" id="parkingOnly">
  <label class="form-check-label" for="parkingOnly">Only show trails with parking</label>
</div>

Accessibility Considerations for Range and Switch Controls

Both controls stay accessible for the same reason: they're native inputs, so screen readers already know how to announce a range's current value and a switch's on/off state — Bootstrap's classes never take that away. Two things to get right yourself:

  • Always pair a range or switch with a visible <label> where the design allows it, connected with matching for/id attributes — exactly like any other form control.
  • When no visible label fits (a compact filter chip, for example), add aria-label directly on the input so assistive tech still has something to announce.
  • For a range slider whose value changes live, update the visible label text (as in the example above, "Max Distance: 10 mi") so sighted and screen-reader users both get the current value, not just the control's existence.
<!-- No visible label: use aria-label instead -->
<input type="range" class="form-range" aria-label="Maximum trail distance in miles" min="0" max="25">

Worked Example: A Trail Filter Panel with Both Controls

The slider and the switch together, as they'd sit inside TrailMap's trail-list filter sidebar:

<div class="panel p-3">
  <div class="mb-3">
    <label for="maxDistance" class="form-label">Max Distance: <span id="maxDistanceValue">10</span> mi</label>
    <input type="range" class="form-range" id="maxDistance" min="0" max="25" value="10">
  </div>
  <div class="form-check form-switch mb-3">
    <input class="form-check-input" type="checkbox" role="switch" id="parkingOnly">
    <label class="form-check-label" for="parkingOnly">Only show trails with parking</label>
  </div>
  <div class="form-check form-switch">
    <input class="form-check-input" type="checkbox" role="switch" id="dogFriendlyOnly" checked>
    <label class="form-check-label" for="dogFriendlyOnly">Dog friendly only</label>
  </div>
</div>

Wiring the live "10" text up to the slider's input event is a one-line vanilla JS addition — the same no-jQuery approach every interactive Bootstrap 5 component in this track uses.

Next up: That wraps up the Forms section. Head to Sass Customization to start tailoring Bootstrap 5's own source instead of overriding it with extra CSS.