Forms & Input
Slider
AvailableSelects a value from a range. Uses aria-valuenow/min/max with keyboard operation.
What Is a Slider?
A slider is a UI control that lets users select a value from a range by moving a thumb horizontally (or vertically). It is commonly used for volume, brightness, playback position, and similar settings.
The key points are: communicate the current value and the allowed range (min/max) to users who cannot see the screen, and make it operable with a keyboard, not just a mouse.
Why Does Accessibility Matter?
- Keyboard-only users. If the thumb is a plain
<div>, it cannot receive focus with Tab and the arrow keys won't change its value. A mouse-only implementation becomes completely inoperable. - Screen reader users. Without the slider role, current value, and range, users have no way to know what percentage they are at or how far they can adjust.
The easiest way to satisfy both requirements is the native<input type="range">. The browser provides keyboard interaction and screen reader announcements out of the box.
Live Demo (Recommended Implementation)
Below is a native <input type="range">.Try operating it without a mouse — focus the thumb and use the arrow keys.
Try it: Tab to the thumb → ← → to adjust by 1, Home / End for min/max, Page Up / Page Down for large steps.
Tip
When a screen reader focuses the thumb, it announces something like "Volume, slider, 40, minimum 0, maximum 100" — role, current value, and range all at once. This is provided automatically by the native element.
Keyboard Interaction
| Key | Action | Priority |
|---|---|---|
| → / ↑ | Increase the value by one step | Required |
| ← / ↓ | Decrease the value by one step | Required |
| Home | Set to the minimum value | Required |
| End | Set to the maximum value | Required |
| Page Up / Page Down | Increase or decrease the value by a larger step | Recommended |
Note
All the keyboard interactions above come for free when you use<input type="range">. If you build a custom slider withrole="slider", you must implement all of them yourself viakeydown handlers.
Required WAI-ARIA Roles & Properties
| Target | Attribute / Role | Meaning |
|---|---|---|
| Native range input | Associate with <label for> | Gives the thumb an accessible name (label). This alone provides the role, value, and range automatically. |
| Custom thumb | role="slider" | Conveys to assistive technologies that this is a slider. |
| Custom thumb | tabindex="0" | Makes the div element focusable via keyboard. |
| Custom thumb | aria-valuemin / aria-valuemax | Defines the allowable range (minimum and maximum). |
| Custom thumb | aria-valuenow | The current value. Must be updated each time the slider is moved. |
| Custom thumb | aria-valuetext | Provides a human-readable representation when the numeric value alone is not meaningful (e.g., "40%", "Medium"). Optional. |
| Custom thumb | aria-label / aria-labelledby | The accessible name of the thumb. Use aria-labelledby to associate with a visible label. |
Recommended Pattern (Good)
Good / Recommended
Start with <input type="range">. Just associate a label and you get the role, value, range, and keyboard interaction all at once.
Markup:
<label for="volume">Volume</label>
<input
type="range"
id="volume"
name="volume"
min="0"
max="100"
step="1"
value="40" />
<output for="volume" id="volume-out">40</output>Display sync (optional — the input itself handles announcements):
const input = document.getElementById('volume');
const out = document.getElementById('volume-out');
// Update the output whenever the value changes (for display; the input handles announcements)
input.addEventListener('input', () => {
out.textContent = input.value;
});Only build a custom slider with role="slider" when native styling truly cannot meet design requirements. In that case, you must provide the value, range, and keyboard interaction entirely on your own.
<!-- Only build a custom slider when native styling won't meet design requirements -->
<span id="bright-label">Brightness</span>
<div
role="slider"
tabindex="0"
aria-labelledby="bright-label"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="40"
aria-valuetext="40%"
class="slider"
id="bright-slider">
<span class="slider-thumb"></span>
</div>const slider = document.getElementById('bright-slider');
const MIN = 0, MAX = 100, STEP = 1, BIG = 10;
function setValue(next) {
const v = Math.min(MAX, Math.max(MIN, next));
slider.setAttribute('aria-valuenow', String(v));
slider.setAttribute('aria-valuetext', v + '%'); // Provide a human-readable unit
slider.style.setProperty('--pos', ((v - MIN) / (MAX - MIN)) * 100 + '%');
}
slider.addEventListener('keydown', (e) => {
const now = Number(slider.getAttribute('aria-valuenow'));
let next = now;
switch (e.key) {
case 'ArrowRight': case 'ArrowUp': next = now + STEP; break;
case 'ArrowLeft': case 'ArrowDown': next = now - STEP; break;
case 'PageUp': next = now + BIG; break;
case 'PageDown': next = now - BIG; break;
case 'Home': next = MIN; break;
case 'End': next = MAX; break;
default: return; // Let unrelated keys pass through
}
e.preventDefault();
setValue(next);
});Anti-pattern (Bad)
Below is a slider built by mouse-dragging a <div>.It works with a mouse, but is completely inoperable via keyboard, and the value is never announced to screen readers.
Try it: pressing Tab does not focus the thumb. Arrow keys have no effect, and screen readers cannot identify this as a slider or read its current value.
<!-- ❌ Anti-pattern: drag-only div -->
<div class="track" onmousedown="startDrag(event)">
<div class="knob" id="knob"></div>
</div>
<!--
No role or tabindex, so it can't receive focus
and can't be operated with a keyboard at all.
No aria-valuenow either, so the current value is never announced.
-->Bad / Avoid
Problems with this implementation:
- Not keyboard-operable — A
divcannot receive focus, so arrow keys cannot move the thumb. - No role conveyed — Without
role="slider", it is not recognized as a slider. - No value or range conveyed — Without
aria-valuenow / valuemin / valuemax, users cannot tell the current percentage. - No accessible name — There is no way to tell what this slider adjusts (e.g., brightness).
Implementation Checklist
- Considered using input type="range" first (highest priority)
- The thumb has an accessible name (label for or aria-label / aria-labelledby)
- The thumb is focusable via keyboard and values change with Left / Right Arrow
- Home / End move to the minimum / maximum values
- For custom implementations, role="slider" + aria-valuemin / aria-valuemax / aria-valuenow are present
- aria-valuenow is updated each time the value changes
- aria-valuetext is provided when the numeric value alone is not meaningful
- Focus is visible (focus ring is not suppressed)
Source (English):Slider Pattern — W3C APG(opens in a new tab)