Accessibility pattern · Form inputs

Slider

A native range input already has the slider role, the arrow keys, Home and End; styling it keeps all of that, so the work is in the look, the words and the steps. aria-valuetext says "₹25,000 per month" instead of a bare number, and a number field beside it lets anyone type the exact amount instead of dragging.

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.

₹25,000 per month

Most two-bedroom flats in Pune rent for ₹18,000 to ₹30,000.

From ₹10,000 to ₹50,000, in steps of ₹500.

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
Arrow Right or Arrow UpRaises the rent by ₹500.
Arrow Left or Arrow DownLowers the rent by ₹500.
Page Up or Page DownRaises or lowers it by ₹5,000, the same in every browser.
Home or EndSets the lowest (₹10,000) or highest (₹50,000) amount.
TabMoves from the slider to the amount field, which always shows the same value.
EnterIn the amount field, checks the number and moves the slider to it, or says what to change.

Screen readers

What it announces

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

WhenExpected announcement
Tab reaches the sliderMaximum rent, slider, ₹25,000 per month. Most two-bedroom flats in Pune rent for ₹18,000 to ₹30,000.
Arrow Right₹25,500 per month
End₹50,000 per month
Tab to the amount fieldOr type an amount (₹ per month), edit text, 50,000
Enter on a typed 23,250Use steps of ₹500, like ₹23,500.

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-slider" data-ap-slider data-region="IN">
  <div class="ap-slider__top">
    <label class="ap-slider__label" for="slider-range">Maximum rent</label>
    <output class="ap-slider__out" for="slider-range" aria-live="off" data-ap-out><span class="ap-slider__amount" data-ap-amount translate="no">₹25,000</span> <span class="ap-slider__per">per month</span></output>
  </div>
  <p class="ap-hint" id="slider-hint">Most two-bedroom flats in Pune rent for ₹18,000 to ₹30,000.</p>

  <div class="ap-slider__track">
    <input class="ap-slider__range" id="slider-range" type="range" min="10000" max="50000" step="500" value="25000" aria-valuetext="₹25,000 per month" aria-describedby="slider-hint" />
    <div class="ap-slider__ticks" aria-hidden="true">
      <span class="ap-slider__tick">₹10k</span>
      <span class="ap-slider__tick">₹20k</span>
      <span class="ap-slider__tick">₹30k</span>
      <span class="ap-slider__tick">₹40k</span>
      <span class="ap-slider__tick">₹50k</span>
    </div>
  </div>

  <div class="ap-field ap-slider__exact">
    <label class="ap-label" for="slider-amount">Or type an amount (₹ per month)</label>
    <p class="ap-hint" id="slider-amount-hint">From ₹10,000 to ₹50,000, in steps of ₹500.</p>
    <div class="ap-slider__money">
      <span class="ap-slider__prefix" aria-hidden="true">₹</span>
      <input class="ap-input ap-slider__input" id="slider-amount" type="text" inputmode="numeric" autocomplete="off" value="25,000" aria-describedby="slider-amount-hint slider-amount-error" />
    </div>
    <p class="ap-error ap-slider__error" id="slider-amount-error" aria-live="polite"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M12 7.5v5.5M12 16.5h.01"/></svg><span data-ap-error></span></p>
  </div>
</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 label names the slider, the output is tied to it with for, and the hint is its description.

  • 1.4.11 Non-text Contrast Level AA

    The thumb's accent ring and the filled part of the track clear 3:1 against the card, and the track has a 3:1 edge.

  • 2.1.1 Keyboard Level A

    The arrow keys, Page Up, Page Down, Home and End all move the slider, and the amount can be typed instead.

  • 2.4.7 Focus Visible Level AA

    The thumb gets a double ring, surface then focus color, so it shows on the fill and off it.

  • 2.5.7 Dragging Movements Level AA

    Dragging is never needed: a tap on the track jumps there, and the number field takes the exact amount.

  • 2.5.8 Target Size (Minimum) Level AA

    The thumb is 28 pixels across, and the whole 28-pixel-high track takes taps.

  • 4.1.2 Name, Role, Value Level A

    The native range input exposes role, minimum, maximum and value; aria-valuetext adds the currency and the period.

Usage

When to use it

Use it

  • Choosing a value in a range where roughly right is fine and seeing the position helps, like a budget, a volume or a radius.
  • Filters people adjust and watch the results of.

Use something else

  • Exact values people already know: a number field is faster and more precise.
  • A small set of named steps, like Small, Medium and Large: use radio buttons.
  • Ranges with two ends, like a price from and to: use a range slider with two thumbs.

Common failures

How it usually goes wrong

  • A slider with no number

    People cannot tell ₹24,500 from ₹25,000 by thumb position. The value is shown beside the label and can be typed.

  • Bare numbers read aloud

    "25000" could be anything. aria-valuetext reads "₹25,000 per month", taken from the same text the screen shows.

  • A thumb too small to grab

    The default thumb is about 16 pixels. This one is 28, with a track that takes taps along its full height.

  • A div rebuilt as a slider

    A custom slider has to rebuild the role, the values and every key. The native range input has them; CSS only changes its look.

  • Focus hidden by the restyle

    appearance: none removes the browser's focus ring along with its look. The thumb gets its own two-ring focus style.

  • Two inputs that disagree

    A number field that does not follow the slider shows two different answers. Each one updates the other as it changes.

Notes

Building it

  • Page Up and Page Down move by different amounts in different browsers (Chrome uses a tenth of the range). The script sets them to ₹5,000 everywhere and says so in the keyboard table.
  • The output element is a live region by default; aria-live="off" stops it repeating what the slider already announces on every step.
  • The fill is a gradient on the track, positioned from a custom property the script sets; Firefox draws it with ::-moz-range-progress instead.
  • aria-valuetext is copied from the output's text, so when the page is translated, the units follow the translation.
  • The tick labels repeat the minimum, maximum and the steps between, so they are hidden from screen readers rather than read before the slider.

Sources: WAI-ARIA Authoring Practices: Slider · MDN: input type="range"

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