Multi-thumb Slider
UI pattern
Users move two thumbs along a shared track. The commonest use is a range, such as the lowest and highest price in a product search, where each thumb sets one end.
Each thumb is a complete slider, with its own role, accessible name, current value, and limits. Nothing wraps the thumbs in a role of their own, so the multi-thumb slider is the two sliders and the relationship between their values.
That relationship is what sets it apart from two sliders placed side by side. When the thumbs set the ends of a range, they can't pass each other: the lower thumb can go no higher than the upper thumb's value, and the upper thumb no lower than the lower thumb's. The limits each thumb reports change as the other thumb moves.
HTML has no multi-thumb range element. Each thumb can be a native <input type="range">, with two of them styled over one track, or a custom slider built from a generic element with role="slider". Either way, keeping each thumb's limits in step with the other thumb's value takes code.
When to Use
Use a multi-thumb slider when:
- Users set two related values on one scale, such as the lowest and highest price, or the start and end of a time window.
- Seeing both values on one track helps users judge the span between them.
Do not use a multi-thumb slider when:
- Users choose a single value. Use a slider.
- Users need more than two values. Use separate controls, each of which makes sense on its own.
- Users need to enter exact numbers they already know. Two spin buttons or text inputs let them type the values directly.
Examples
Code in this section is meant to demonstrate techniques rather than requirements and lacks features needed in a production application.
Custom ARIA Price Range Slider
This build sets a price range between $0 and $500 in steps of $10. Each thumb is a custom slider with its own visible label, referenced by aria-labelledby, so screen reader users hear "Minimum price" or "Maximum price" along with the value. The thumbs are children of the track in the order they sit on screen, so the lower thumb always comes first in the tab order.
Each thumb's limits start out coupled in the markup. The lower thumb's aria-valuemax is the upper thumb's current value, and the upper thumb's aria-valuemin is the lower thumb's.
Price range slider HTML
<div class="price-range">
<span id="min-price-label">Minimum price</span>
<span id="max-price-label">Maximum price</span>
<div class="range-track" data-step="10">
<div
class="range-thumb"
role="slider"
tabindex="0"
aria-labelledby="min-price-label"
aria-valuemin="0"
aria-valuemax="400"
aria-valuenow="100"
aria-valuetext="$100"
></div>
<div
class="range-thumb"
role="slider"
tabindex="0"
aria-labelledby="max-price-label"
aria-valuemin="100"
aria-valuemax="500"
aria-valuenow="400"
aria-valuetext="$400"
></div>
</div>
</div>CSS places the thumbs on the track and gives each a visible focus indicator. touch-action: none on the track lets a drag on a touch screen move a thumb rather than scroll the page.
Price range slider CSS
.range-track {
position: relative;
height: 4px;
touch-action: none;
}
.range-thumb {
position: absolute;
top: 50%;
width: 24px;
height: 24px;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.range-thumb:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}Script handles each thumb the way Slider › Custom ARIA Slider describes, and adds the coupling. Every change goes through setValue(), which clamps the value to the thumb's declared limits, and then update() rewrites the other thumb's limit to match.
The clamp reads the same attributes assistive technology reports, so the limits users are told about are the limits the thumb enforces, and Home and End take a thumb as far as the other thumb allows.
Price range slider JavaScript
class PriceRangeSlider {
constructor(track) {
this.track = track;
this.thumbs = [...track.querySelectorAll('[role="slider"]')];
const [lower, upper] = this.thumbs;
this.min = this.limit(lower, "aria-valuemin");
this.max = this.limit(upper, "aria-valuemax");
this.step = Number(track.dataset.step) || 1;
this.thumbs.forEach((thumb) => {
thumb.addEventListener("keydown", (event) => {
this.onKeydown(event, thumb);
});
thumb.addEventListener("pointerdown", (event) => {
// Keep focus on the thumb being dragged
event.preventDefault();
thumb.setPointerCapture(event.pointerId);
thumb.focus();
});
thumb.addEventListener("pointermove", (event) => {
if (thumb.hasPointerCapture(event.pointerId)) {
this.moveTo(event, thumb);
}
});
});
this.update();
}
limit(thumb, name) {
return Number(thumb.getAttribute(name));
}
value(thumb) {
return this.limit(thumb, "aria-valuenow");
}
// Snap to the step, then clamp to this thumb's current limits,
// which the other thumb's value has already set
setValue(thumb, value) {
const steps = Math.round((value - this.min) / this.step);
const snapped = this.min + steps * this.step;
const clamped = Math.min(
this.limit(thumb, "aria-valuemax"),
Math.max(this.limit(thumb, "aria-valuemin"), snapped)
);
thumb.setAttribute("aria-valuenow", String(clamped));
thumb.setAttribute("aria-valuetext", `$${clamped}`);
this.update();
}
// Each thumb's value is the other thumb's limit, one step
// short of the far end of the scale
update() {
const [lower, upper] = this.thumbs;
const top = Math.min(this.value(upper), this.max - this.step);
const bottom = Math.max(this.value(lower), this.min + this.step);
lower.setAttribute("aria-valuemax", String(top));
upper.setAttribute("aria-valuemin", String(bottom));
this.thumbs.forEach((thumb) => {
const span = this.max - this.min;
const ratio = (this.value(thumb) - this.min) / span;
thumb.style.left = `${ratio * 100}%`;
});
}
onKeydown(event, thumb) {
const changes = {
ArrowRight: this.step,
ArrowUp: this.step,
ArrowLeft: -this.step,
ArrowDown: -this.step,
};
if (event.key in changes) {
this.setValue(thumb, this.value(thumb) + changes[event.key]);
} else if (event.key === "Home") {
this.setValue(thumb, this.limit(thumb, "aria-valuemin"));
} else if (event.key === "End") {
this.setValue(thumb, this.limit(thumb, "aria-valuemax"));
} else {
return;
}
// Keep the page from scrolling on the same key press
event.preventDefault();
}
moveTo(event, thumb) {
const rect = this.track.getBoundingClientRect();
const ratio = (event.clientX - rect.left) / rect.width;
this.setValue(thumb, this.min + ratio * (this.max - this.min));
}
}
document.querySelectorAll(".range-track").forEach((track) => {
new PriceRangeSlider(track);
});Structure and Semantics
Users reach a multi-thumb slider one thumb at a time, so each thumb has to convey everything a single slider does: the slider role, an accessible name, its current value, and its limits, all on the element users focus. No role describes the thumbs as a set, and the element that holds them needs none.
Two things go beyond a single slider. Each thumb's name says which value it sets, so the two can be told apart. And when the thumbs set the ends of a range, each thumb's limits track the other thumb's value, so the range a thumb reports is the range it can reach.
A custom thumb carries these attributes. A native <input type="range"> thumb carries its role implicitly and sets its value and limits with value, min, and max rather than the ARIA properties, following the same coupling.
| Attribute | How to use |
|---|---|
role | Set to "slider" on each thumb. Leave the element that holds the thumbs without a role. |
aria-valuenow | Set on each thumb to its current value, and update it every time the value changes. |
aria-valuemin | On the lower thumb, set to the lowest value on the scale. On the upper thumb, set to the lower thumb's current value or one step above the lowest value, whichever is higher, and update it as that thumb moves. |
aria-valuemax | On the upper thumb, set to the highest value on the scale. On the lower thumb, set to the upper thumb's current value or one step below the highest value, whichever is lower, and update it as that thumb moves. |
aria-labelledby | Set on each thumb to the id of its visible label, such as "Minimum price". Use aria-label instead when there's no visible label. |
aria-valuetext | Optional. Set on each thumb to a readable form of its value, such as "$100", when the number alone isn't meaningful. |
Multi Thumb Slider
A multi-thumb slider has two thumbs, and each one is built as a complete slider. Two is the shape users meet most: two values that make sense together, one at each end of a span.
Exposes every thumb as a slider
Reported as: Multi-thumb slider does not have exactly two thumbs
A multi-thumb slider should have two thumbs, each exposed as a slider with its own name, value, and limits.
When a track carries more than two thumbs, assistive technology users meet a string of sliders with no way to tell how the values relate or which one matters. When the extra thumbs lack the slider semantics the first two have, part of the control is missing for those users, while the extra thumbs' values go on shaping the result.
Build the control with exactly two thumbs from one component. If more values are needed, give each its own control.
Severity: Critical
Role
Each thumb of a multi-thumb slider carries the slider role, on the element users focus and move. No ARIA role describes the multi-thumb slider as a whole, so the element holding the thumbs carries none. Assistive technology reports the role on each thumb, so users know they've reached a control whose value they can change.
Has the correct role
Reported as: Element has incorrect role
Each thumb of a multi-thumb slider must expose the slider role.
When a custom thumb is built from a <div> without the role, or carries a different one, assistive technology presents it as plain content or as the wrong kind of control. Users aren't told that it has a value or that the arrow keys change it, and its value properties go unreported.
Use a native <input type="range"> for each thumb, or add role="slider" to each custom thumb.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Applies role to the correct element
Reported as: Incorrect root element
Sanity check. Evinced checks that the slider role sits on each thumb users focus and move, not on an element around it or inside it. This check runs on every UI pattern, not only on multi-thumb sliders.
Each thumb should carry the slider role on the element users focus and move, not on the track around it or an element inside it.
When the role sits on a wrapper while the tab stop and key handling sit on the thumb, assistive technology reports the focused thumb, which has no role or value. Users land on the thumb and aren't told what it is or where it's set, even though role="slider" is in the markup.
Put the role, the accessible name, the value properties, the tab stop, and the key handling on each thumb. Keep the track a plain container for layout and pointer input.
Accessibility Label
Each thumb's accessible name says what its value is for, in words that work without the screen around it, such as "Minimum price" rather than "Minimum" or "From". Sighted users read the purpose from the label above the track and from which end the thumb sits at. A screen reader user reaches the thumb on its own and hears only what its markup provides.
Names each thumb in its own markup
Reported as: Missing contextual labeling
Each thumb of a multi-thumb slider must carry its own accessible name, not rely on a label or position around it.
When a thumb's purpose shows only in its surroundings, such as a "Price" heading over the track and its position at the left or right end, assistive technology reports the thumb as a slider and a value with nothing to say what the value sets. Screen reader users can't tell a price from any other number, or the lower end from the upper one.
Point each thumb's aria-labelledby at its visible label, as the example does, or associate a <label> with a native thumb. Use aria-label only when there's no visible text to reference.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 1.3.1 Info and Relationships (A) | Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text. |
| EN 301 549 | 9.1.3.1 Info and Relationships | Same as the WCAG text. |
Sliders Accessibility Labels
Each thumb of a multi-thumb slider has its own accessible name, and the name says which value that thumb sets, such as "Minimum price" and "Maximum price". Screen reader users usually reach one thumb at a time, so each name has to make sense on its own.
Identifies each member by name
Reported as: Group members missing accessible names
Each thumb of a multi-thumb slider must have an accessible name.
When a thumb has no name, screen reader users hear only its role and value, such as "slider, 100", with no indication of what the number is for or which end of the range it sets. Voice control users have no name to speak.
Point each thumb's aria-labelledby at its visible label, or give it an aria-label when there's no visible text. On a native thumb, associate a <label> with the input.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Tells one member from another
Reported as: Group contains duplicate names
The two thumbs of a multi-thumb slider should have different accessible names.
When both thumbs share a name, usually because both reference the one visible label for the whole control, such as "Price", screen reader users hear the same announcement on each. They can't tell which thumb sets the minimum and which sets the maximum without moving one and working out what changed. Voice control users can't pick either thumb by name.
Name each thumb for the value it sets, such as "Minimum price" and "Maximum price". To keep the control's label in each name, point aria-labelledby at the shared label and the thumb's own text together.
Severity: Serious
aria-valuetext
When a thumb's number doesn't say what the value means, aria-valuetext gives assistive technology a readable form of it, such as "$100" for a value of 100, as in the example. Assistive technology reports the text in place of the number, so each thumb's text changes every time its value does. It's allowed on a native <input type="range"> as well as on a custom thumb.
Uses value text to add meaning
Reported as: Meaningful aria-valuetext
A thumb's aria-valuetext should say something its number doesn't.
When aria-valuetext repeats the value, such as "100" for a value of 100, screen reader users hear nothing the number didn't already tell them. The text also replaces the number in what assistive technology reports, so if it isn't updated with the value, users hear a value the thumb no longer holds.
Leave aria-valuetext off when the number speaks for itself. When it needs a unit or a label, set text that adds it, and update it in the same step as the value, as setValue() does in the example.
Severity: Minor
Values
Each thumb reports its current value and its limits the way any slider does. When the thumbs set the ends of a range, one thumb's value is the other thumb's limit: the upper thumb's minimum is at least the lower thumb's value, and the lower thumb's maximum is at most the upper thumb's value. The lower value then never exceeds the upper one.
Updates limits as a thumb moves
Reported as: Multithumb slider conflicting values
When one thumb's value limits the other, the dependent thumb's declared limits must update every time that value changes.
When each thumb keeps declaring the limits of the whole scale while code stops it at the other thumb, the two reports disagree. Screen reader users are told the lower thumb can reach the top of the scale, and then pressing End or an arrow key stops it partway with nothing to say why. The control reads as broken rather than as having reached a boundary.
Write the other thumb's limit in the same step that writes a thumb's value, on every route that changes it, including keyboard, pointer, and any reset. Clamp each value to the thumb's declared limits, so what's reported is what's enforced.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Leaves native values to HTML
Reported as: Avoid ARIA values on native inputs
A native <input type="range"> thumb must not also carry aria-valuenow, aria-valuemin, or aria-valuemax.
When a native thumb has ARIA value properties alongside its own value, min, and max, the two can disagree. The browser updates value as the user moves the thumb, while the ARIA properties keep whatever code last wrote, so screen reader users can hear a value or a limit the thumb no longer has.
Remove the ARIA value properties, and write the coupled limit to the other thumb's min or max. Keep aria-valuetext if the number needs explaining, since it has no native counterpart.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Orders its limits low to high
Reported as: Min value is greater than the max value
Each thumb's minimum must be lower than its maximum.
When a thumb's minimum is greater than its maximum, assistive technology can't place its value within the range. Screen reader users hear a bare number, or a negative percentage, with no sense of where it falls.
Keep the lower thumb's value at or below the upper thumb's before writing either thumb's limits, including after a reset or a change from data, and declare each thumb's range low to high.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Reports its value and its limits
Reported as: Missing range attributes
Each thumb must report its current value and the limits of its range to assistive technology.
When a custom thumb shows its value only through its position on the track and never sets aria-valuenow, screen reader users learn they're on a slider but not what it reads. Missing limits fail more quietly: assistive technology assumes a range of 0 to 100, so screen reader users hear a price of $100 on a $0 to $500 scale as 100 percent.
Set aria-valuenow, aria-valuemin, and aria-valuemax on each custom thumb, and update them in the same code that moves the thumbs. A native <input type="range"> reports all three from its value, min, and max.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Spans more than a single value
Reported as: Range has equal min and max values
Each thumb's range must have room for more than one value, with a maximum above its minimum.
When a thumb's minimum and maximum are equal, the thumb can hold only one value. Users can press keys or drag it with no effect, and they can't tell a thumb fixed at one value from one that has stopped responding.
Check that each thumb's limits differ before writing them. When the thumbs set a range, stop the lower thumb's maximum one step below the top of the scale and the upper thumb's minimum one step above the bottom, so thumbs that meet at an end still leave each one room to move. When the whole scale has a single value, show it as text, or disable the slider and say why.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
aria-invalid
Each thumb keeps its value inside its own limits, and those limits move with the other thumb. A native <input type="range"> clamps its value on its own, so aria-invalid has little to do on a thumb. When a thumb's value does fall outside its limits, which is itself an error, aria-invalid="true" reports it, and it's removed once the value is back in range.
Flags invalid only when out of range
Reported as: False aria-invalid value
A thumb must not report a value inside its limits as invalid.
When aria-invalid="true" stays on a thumb after its value returns to range, or is written into the markup as a hook for error styling, screen reader users are told a correct value is wrong. They may keep adjusting an end of the range that was fine, or abandon a filter they set correctly.
Set aria-invalid from the thumb's current value each time it changes: true only while the value is outside its limits, and removed otherwise. Drive error styling from a class, not from the attribute.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Keeps the value within its bounds
Reported as: Value out of range
Each thumb's reported value must sit at or between its declared minimum and maximum.
When a value comes from saved data or a reset that ignores the other thumb, a thumb can report a value outside its own limits, such as a minimum price of $450 while its maximum is $400. Screen reader users hear a range that contradicts itself, and stepping from that value behaves unpredictably.
Send every change through one function that clamps the value to the thumb's limits and then updates the other thumb's limit, as setValue() and update() do in the example.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Disabled
A disabled multi-thumb slider conveys the state on its thumbs, each one in the way a single slider of the same construction does. The element that holds the thumbs has no role, so it has no disabled state to set, and disabling the control means disabling each thumb.
Disables only when role allows
Reported as: Component does not support disabled state
A multi-thumb slider must declare its disabled state on the thumbs, not on the element that holds them.
When disabled or aria-disabled is set on the container around the thumbs, it has nowhere to go. Assistive technology may drop it or misreport the container, while both thumbs stay in the tab order and go on responding to input. The control looks grayed out on screen, yet screen reader users are told both thumbs are available.
Take the attribute off the container and set the disabled state on each thumb: disabled on a native thumb, and aria-disabled="true" on a custom one.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Disables only elements that support it
Reported as: Element type does not support disabled state
An element in a multi-thumb slider must carry disabled or aria-disabled only when its role supports a disabled state.
When the state sits on an element whose role has none, such as a wrapper, a list item or a heading, assistive technology may drop it or report it on something users can't operate, while the control that really is unavailable is still reported as available.
Remove the attribute from that element, and set the disabled state on the control itself, using an element or role that supports it, such as a native <button>.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Avoids conflicting disabled states
Reported as: Conflicting disabled values
Each thumb must have a single, consistent disabled state.
When a thumb sets its disabled state two ways and the values disagree, such as the disabled attribute alongside aria-disabled="false", assistive technology may report a state the browser doesn't enforce. Users can't tell whether they're able to change that end of the range.
Set the disabled state once on each thumb: the disabled attribute on a native thumb, or aria-disabled on a custom one, never both.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Uses aria-disabled rather than disabled
Reported as: Element does not support the disabled attribute
A custom thumb must convey its disabled state with aria-disabled, not the disabled attribute.
When disabled is set on the <div> or <span> behind a custom thumb, the browser ignores it. The thumb stays in the accessibility tree as an ordinary, available control, so users are told they can change a value the design means to be locked, even when styling grays it out.
Set aria-disabled="true" on each custom thumb instead, and ignore key and pointer input in code while it's set.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Relationships
A multi-thumb slider is two sliders, and each thumb relates to its label and its supporting text the way any slider does, by reference. Their values and limits, not an attribute, carry the relationship between the thumbs.
Place the thumbs as DOM children of the element that holds them, in the order they appear on the track. Their grouping and their tab order then come from the document structure, and no ARIA relationship attribute is needed.
aria-owns
A thumb is a slider, so it can't own other elements: its children are presentational. The element that holds the thumbs could use aria-owns to claim a thumb that sits elsewhere in the DOM, but support for the attribute is uneven across assistive technologies. Move the thumb into the container instead, and reserve aria-owns for a thumb that can't be placed there.
Avoids aria-owns
Reported as: Avoid aria-owns
General check. Evinced checks that the thumbs and the element that holds them don't use aria-owns to claim elements elsewhere in the document as their children. This check runs on every UI pattern, not only on multi-thumb sliders.
A multi-thumb slider should not use aria-owns on its thumbs, and the element that holds them should use it only for a thumb that can't be placed inside it.
A thumb allows only presentational children, so an owned label, value display, or tick mark becomes presentational too. When the container owns a thumb from elsewhere in the DOM, browsers that don't honor the attribute leave the thumb outside the control, so screen reader users can meet the thumbs apart or in an order that doesn't follow the track.
Remove aria-owns from the thumbs, and from the container wherever the thumbs can be moved into it, placing them in their order on the track. Reference each thumb's label with aria-labelledby and any supporting text with aria-describedby.
Owns only conforming roles
Reported as: Invalid aria-owns values
General check. Evinced checks that aria-owns on the element holding the thumbs references only elements with the slider role. This check runs on several UI patterns.
When the element holding a multi-thumb slider's thumbs uses aria-owns, it should reference only the thumbs, each carrying the slider role.
When the attribute also lists a label, a value display, or tick marks, or references a thumb another element already owns, assistive technology builds a structure that matches neither the markup nor the screen. Users can reach the owned elements in an order that doesn't follow the track, or meet the same thumb twice.
List only the two thumbs, in their order on the track, and keep each owned by one element. Better still, move them into the container and remove aria-owns.
Operation
Users operate each thumb the way they operate a single slider. With a pointer, they drag the thumb along the track. With a keyboard, the arrow keys, Home, and End change the value of the thumb that has focus, as Keyboard Navigation describes.
Moving between the thumbs is what a multi-thumb slider adds. The Tab key moves focus from the first thumb to the second, and Shift + Tab moves it back. That order follows the DOM and stays fixed whatever the thumbs' values, so a thumb never changes places in the tab order as it moves.
When the thumbs set the ends of a range, each key press stops at the other thumb, and Home and End take a thumb to the limits it reports. Home takes the upper thumb down to the lower thumb's value, and End takes the lower thumb up to the upper thumb's, each stopping one step short of the far end of the scale.
Focus Sequence
Each thumb of a multi-thumb slider is its own tab stop, reachable whenever it's enabled. Keyboard users find the second thumb only by tabbing to it, so a thumb missing from the tab order leaves them with a range that seems to have one adjustable end.
Gives each thumb a tab stop
Reported as: Second thumb not focused
Each thumb of a multi-thumb slider must be its own stop in the tab order.
When the Tab key reaches the first thumb and then leaves the control, keyboard users can't set the other end of the range, although pointer users can drag it.
Give each custom thumb tabindex="0", rather than one focusable wrapper around both, and set the order by placing the thumbs in the DOM in their order on the track rather than with positive tabindex values.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Reachable by keyboard
Reported as: Keyboard accessible
A multi-thumb slider must be reachable with the Tab key.
When the thumbs of a custom multi-thumb slider are built from <div> elements with drag handling but no tabindex, keyboard users can't reach the control, so they can't set either end of the range, however correct its roles and value properties are.
Use a native <input type="range"> for each thumb, which is focusable on its own, or add tabindex="0" to each custom thumb.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Skips tab order when disabled
Reported as: Disabled element focusable
A thumb that presents as disabled should not be in the tab order.
When a disabled thumb keeps its tab stop, keyboard users land on a control whose value they can't change, and a disabled multi-thumb slider costs them two such stops.
Use the disabled attribute on a native thumb, which removes it from the tab order. On a custom thumb, set tabindex="-1" while aria-disabled="true" is set, and restore tabindex="0" when it becomes available.
Keeping a disabled custom thumb focusable is a recognized alternative, since users can then find it, learn its value, and learn that it's unavailable. If you take that route, expose the disabled state and ignore key input.
Severity: Minor
Element that carries role receives focus
Reported as: Focusable offset
Each thumb must receive focus on the element that carries the slider role, not on one of its descendants.
When the role sits on a wrapper and focus lands on an element inside it, assistive technology reports the focused element, which has no role or value. Users tabbing through the page aren't told they've reached a slider or where it's set.
Put the role and the tab stop on the same element for each thumb, and remove focusability from any other part. A native <input type="range"> has one element, so the two can't separate.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Context Change
Moving a thumb changes only its value, and with it the other thumb's limit. Anything more, such as submitting a form, loading another page, or moving focus, waits for an explicit control such as an Apply button. The exception is a change of context that text before the slider tells users to expect. Every arrow key press and every point of a drag is a change, so a range can pass through many values before users settle on both ends.
Keeps context when a value changes
Reported as: Unexpected context change on input
Changing a thumb's value must not change the context unless users are told beforehand that it will.
When a value change reloads the page or moves focus, screen reader and screen magnifier users may not notice that the page changed, and keyboard users can't step past the first value they reach or move on to the second thumb. A product filter that reloads results on each change is the common case.
Update the page in place and leave focus on the thumb. Start the search or submission from an explicit control, or, when the change of context is intended, say so in text that comes before the slider.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 3.2.2 On Input (A) | Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component. |
| EN 301 549 | 9.3.2.2 On Input | Same as the WCAG text. |
| Section 508 | 1194.22(n) | When electronic forms are designed to be completed on-line, the form shall allow people using assistive technology to access the information, field elements, and functionality required for completion and submission of the form, including all directions and cues. |
| Section 508 | 1194.21(l) | When electronic forms are used, the form shall allow people using assistive technology to access the information, field elements, and functionality required for completion and submission of the form, including all directions and cues. |
Keyboard Navigation
Each thumb changes its value from the keyboard in steps: Right Arrow and Up Arrow add one, and Left Arrow and Down Arrow take one away. Every change stays inside that thumb's current limits, which the other thumb sets. A custom thumb handles the keys in a keydown listener and calls preventDefault() for each key it handles, so the arrow keys, Home, and End don't also scroll the page.
Changes value with the keyboard
Reported as: Slider value unchanged with keyboard
Each thumb of a multi-thumb slider must change its value when the user presses an arrow key.
When custom thumbs handle only pointer input, they can take focus and expose their role and value correctly while every key press leaves the value where it was. Setting the two ends of the range is the control's only job, so people who don't use a pointer, including many screen reader, switch, and keyboard users, can't use it.
Add a keydown handler to each thumb that changes its value by one step for each arrow key, and update aria-valuenow, and aria-valuetext when the thumb has it, with the thumb's position.
Severity: Critical
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Keeps the value in range
Reported as: aria-valuenow out of range
A thumb's value must stay between its minimum and maximum as the user operates it with the keyboard.
When a key handler adds or subtracts a step without checking the thumb's current limits, holding an arrow key walks aria-valuenow past the other thumb or off the end of the scale. Screen reader users hear a lower price above the upper one, or a value the scale can't contain.
Clamp every change to the thumb's current limits in one function that all the keys share, and write the clamped value to aria-valuenow and the thumb's position together.
Severity: Serious
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 4.1.2 Name, Role, Value (A) | For all user interface components, the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to assistive technologies. |
| EN 301 549 | 9.4.1.2 Name, Role, Value | Same as the WCAG text. |
| Section 508 | 1194.21(d) | Sufficient information about a user interface element including the identity, operation and state of the element shall be available to assistive technology. When an image represents a program element, the information conveyed by the image must also be available in text. |
Sets the maximum with End
Reported as: Slider does not support End key
A thumb must set its value to its maximum when the user presses the End key.
When End isn't handled, it scrolls the page instead. Moving the upper thumb to the top of the scale, or the lower thumb up to the upper one, then takes one arrow key press per step, and screen reader users hear every value on the way.
Handle End in the same keydown listener as the arrow keys, set the value to the thumb's current maximum, and call preventDefault() so the page doesn't scroll.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Sets the minimum with Home
Reported as: Slider does not support Home key
A thumb must set its value to its minimum when the user presses the Home key.
When Home isn't handled, it scrolls the page and leaves the value where it was. On the example's scale, taking the lower thumb back to $0 can take up to 49 key presses, which is impractical for anyone who can't drag it.
Handle Home alongside End and the arrow keys, set the value to the thumb's current minimum, and call preventDefault() so the page doesn't scroll.
Severity: Minor
| Standard | Success Criterion | Reference |
|---|---|---|
| WCAG 2.2 | 2.1.1 Keyboard (A) | All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes, except where the underlying function requires input that depends on the path of the user's movement and not just the endpoints. |
| EN 301 549 | 9.2.1.1 Keyboard | Same as the WCAG text. |
| Section 508 | 1194.21(a) | When software is designed to run on a system that has a keyboard, product functions shall be executable from a keyboard where the function itself or the result of performing a function can be discerned textually. |
| 508 Trusted Tester | 2.1.1-keyboard-access, 4.A | All functionality can be accessed and executed using only the keyboard. |
Event Listener Type
When script in a multi-thumb slider responds to keys, it listens for keydown or keyup. The keypress event is deprecated, and browsers don't fire it for many keys, including the arrow keys, Home, End and Escape.
Avoids the keypress event
Reported as: Deprecated keypress listener
A multi-thumb slider's key handling should use keydown or keyup, not keypress.
When a handler listens for keypress, keys the browser doesn't send through that event go unanswered, and keyboard users find the multi-thumb slider ignoring keys it's meant to respond to.
Replace each keypress listener with a keydown listener, and read the key from event.key.
Severity: Minor
FAQ
Which WCAG success criteria do the multi-thumb slider issues relate to?
Most relate to 4.1.2 Name, Role, Value: each thumb's slider role and accessible name, reporting each thumb's value and limits, ordering the limits and keeping each range wider than one value, keeping each value inside its limits, updating one thumb's limits as the other moves, using aria-invalid only for a value out of range, leaving native values to HTML, and how and where the disabled state is declared.
Naming each thumb in its own markup, rather than leaving its purpose to the label and position around it, relates to 1.3.1 Info and Relationships. Reaching the slider, giving each thumb a tab stop, taking focus on the element with the role, and changing each value with the arrow keys, Home, and End relate to 2.1.1 Keyboard.
Keeping the context when a value changes relates to 3.2.2 On Input. Having two thumbs, distinct thumb names, value text that adds meaning, and keeping a disabled thumb out of the tab order map to no WCAG success criterion and are best practices.
Why are some multi-thumb slider issues marked as Critical and others as Minor?
Severity reflects how much an issue gets in users' way. Critical issues stop users outright:
- A thumb with no role, name, value, or limits
- Limits that are inverted, equal, or out of step with the other thumb
- ARIA values that contradict a native thumb
- Extra thumbs
- A thumb keyboard users can't reach
- A thumb whose value the arrow keys don't change
Serious issues confuse users though tasks are still possible:
- A thumb named only by the label and position around it
- Two thumbs with the same name
- A value outside a thumb's limits, or a value in range reported as invalid
- A disabled state set on the container or declared the wrong way
- A value change that loads a page or submits the form
Minor issues are smaller obstacles:
- A disabled thumb in the tab order
- Focus on an element other than the one with the role
- Home and End that don't move a thumb to its limits
- Value text that repeats the number
The aria-owns checks and the check on where the role sits carry no severity: Evinced reports them as general and sanity checks.
Related Patterns
- Slider: a single thumb for a single value, and the build each thumb follows
- Spin Button: a value users type as well as step, two of which can set a range
- Text Input: free entry of an exact value