Accessibility pattern · Form inputs

Range slider

Each thumb is its own slider with its own name, and each one's limit is the other's value, so the two can meet but never cross. Two text fields set the same values by typing, and the result count is announced once the thumbs stop moving, not on every step.

WCAG criteria
7
Keyboard rules
6
Checked with
axe, keyboard and the inspector

Live demo

Try it

Use it with a mouse, a keyboard or a screen reader. The inspector beside it shows what the browser tells assistive technology as you go: focus, state changes and announcements.

Price

Wireless headphones, prices include GST

Or type the range

Whole rupees from ₹0 to ₹20,000. A value past the other end is moved to meet it.

23 products match

The demo works without JavaScript only as far as its HTML does; the inspector needs JavaScript.

Keyboard

Keys it answers to

Every action works without a pointer.

KeyWhat it does
TabMoves to the minimum thumb, then the maximum thumb, then the two fields.
Arrow Right or Arrow UpRaises the focused thumb by ₹500, stopping at the other thumb or the end of the track.
Arrow Left or Arrow DownLowers the focused thumb by ₹500, stopping at the other thumb or the start.
Page Up or Page DownMoves the focused thumb by ₹2,000.
Home or EndMoves the focused thumb to the lowest or highest value it can take.
EnterIn a field, applies the typed price; leaving the field does the same.

Screen readers

What it announces

Written from the roles, names and states in the markup.

WhenExpected announcement
Tab reaches the first thumbPrice, group. Minimum price, slider, ₹2,000
Arrow Right moves it₹2,500
The thumbs stop moving for half a second20 products match
Tab reaches a fieldMinimum (₹), edit text, 2,500. Whole rupees from ₹0 to ₹20,000. A value past the other end is moved to meet it.
Enter applies a typed price14 products match

These are expected announcements, not recordings. Wording and order differ between screen readers and browsers.

Code

Copy the code

The exact files this demo runs on. The styles are served with a prefix that keeps this site's own styles out of the demo; what you copy is the original.

The markup

<div class="ap-range-slider" data-ap-range-slider data-min="0" data-max="20000" data-step="500" data-page="2000">
  <div class="ap-range-slider__top">
    <h3 class="ap-range-slider__label" id="range-slider-label">Price</h3>
    <p class="ap-range-slider__sub">Wireless headphones, prices include GST</p>
  </div>

  <div class="ap-range-slider__bars" data-ap-bars aria-hidden="true"></div>
  <div class="ap-range-slider__track" data-ap-track role="group" aria-labelledby="range-slider-label">
    <span class="ap-range-slider__rail" aria-hidden="true"></span>
    <span class="ap-range-slider__fill" data-ap-fill aria-hidden="true"></span>
    <span class="ap-range-slider__thumb" data-ap-thumb="min" role="slider" tabindex="0" aria-label="Minimum price" aria-valuemin="0" aria-valuemax="12000" aria-valuenow="2000" aria-valuetext="₹2,000"></span>
    <span class="ap-range-slider__thumb" data-ap-thumb="max" role="slider" tabindex="0" aria-label="Maximum price" aria-valuemin="2000" aria-valuemax="20000" aria-valuenow="12000" aria-valuetext="₹12,000"></span>
  </div>
  <div class="ap-range-slider__scale" aria-hidden="true"><span>₹0</span><span>₹20,000</span></div>

  <fieldset class="ap-range-slider__fields">
    <legend class="ap-range-slider__legend">Or type the range</legend>
    <div class="ap-range-slider__pair">
      <div class="ap-field">
        <label class="ap-label" for="range-slider-from">Minimum (₹)</label>
        <div class="ap-range-slider__money">
          <span aria-hidden="true">₹</span>
          <input class="ap-input" id="range-slider-from" data-ap-field="min" type="text" inputmode="numeric" autocomplete="off" value="2,000" aria-describedby="range-slider-hint" />
        </div>
      </div>
      <span class="ap-range-slider__dash" aria-hidden="true">–</span>
      <div class="ap-field">
        <label class="ap-label" for="range-slider-to">Maximum (₹)</label>
        <div class="ap-range-slider__money">
          <span aria-hidden="true">₹</span>
          <input class="ap-input" id="range-slider-to" data-ap-field="max" type="text" inputmode="numeric" autocomplete="off" value="12,000" aria-describedby="range-slider-hint" />
        </div>
      </div>
    </div>
    <p class="ap-hint ap-range-slider__hint" id="range-slider-hint">Whole rupees from ₹0 to ₹20,000. A value past the other end is moved to meet it.</p>
  </fieldset>

  <p class="ap-range-slider__result">
    <span class="ap-range-slider__n" data-ap-count translate="no">23</span>
    <span data-ap-unit>products match</span>
  </p>
  <p class="ap-range-slider__sr" role="status" data-ap-say></p>
</div>

WCAG 2.2

What it meets

The success criteria this pattern takes care of, and how.

  • 1.3.1 Info and Relationships Level A

    The two sliders sit in a group named by the Price heading, and the fields share a fieldset with its own legend.

  • 2.1.1 Keyboard Level A

    Arrow, Page Up, Page Down, Home and End move each thumb; the fields set exact values by typing.

  • 2.5.7 Dragging Movements Level AA

    Dragging is never the only way: a click on the track moves the nearer thumb there, and the keys and fields do the rest.

  • 2.4.7 Focus Visible Level AA

    Each thumb shows a ring offset from its edge when it has keyboard focus, separate from its hover halo.

  • 4.1.2 Name, Role, Value Level A

    Each thumb has role=slider, its own name, and aria-valuenow, aria-valuetext, aria-valuemin and aria-valuemax kept current.

  • 4.1.3 Status Messages Level AA

    The result count is announced in a status message after movement stops, without taking focus.

  • 1.4.11 Non-text Contrast Level AA

    The thumbs' borders and the filled range clear 3:1 against the card, so the chosen range is visible without color judgement.

Usage

When to use it

Use it

  • Filtering by a range where the rough shape matters more than exact numbers, like price, distance or duration.
  • Ranges people adjust while watching a count or a list change.

Use something else

  • A single value: use a one-thumb slider or a number input.
  • Exact values people already know, like a date of birth or a pin code: give them fields only.
  • Very large or open-ended ranges, where one pixel of travel jumps by thousands.

Common failures

How it usually goes wrong

  • Two thumbs with the same name

    Two sliders both called "Price" leave people guessing which end they are moving. Each thumb here says Minimum or Maximum.

  • Thumbs that cross over

    If the minimum can pass the maximum, the values swap meaning mid-drag. Each thumb's limit is the other's value, and aria-valuemin and aria-valuemax say so.

  • A number read without its unit

    aria-valuenow alone is read as a bare 2000. aria-valuetext adds the rupee sign and grouping, so it is heard as a price.

  • Dragging as the only way in

    People with tremors, switch users and voice users cannot drag precisely. Keys, a click on the track and two plain fields all set the same values.

  • Every step announced

    A live region that speaks on each pixel of a drag drowns out the slider itself. The count here waits for half a second of stillness.

  • Fields that fight the slider

    Fields that validate on every keystroke reset a half-typed number. These apply on Enter or when focus leaves, and correct only out-of-range values.

Notes

Building it

  • Keep each thumb's aria-valuemin and aria-valuemax in step with the other thumb: that is how assistive technology learns where the thumb can go.
  • Pointer events with setPointerCapture keep a drag going when the pointer leaves the track, and touch-action: none stops the page scrolling under a finger.
  • Arrow keys snap to the next ₹500 mark, so a typed ₹2,450 moves to ₹2,500 rather than ₹2,950.
  • The histogram above the track is decoration and is hidden from assistive technology; the count below says the same thing in words.
  • In a right-to-left page, flip the arrow keys and the track direction together.

Sources: WAI-ARIA Authoring Practices: Slider (Multi-Thumb) · Understanding WCAG 2.2: Dragging Movements

Checked with axe in light and dark themes, at desktop and phone widths, and by keyboard. Not yet tested with every screen reader and browser pair. Report a correction