Component
Slider
A duration, count or amount chosen by dragging along a bounded scale, as one value or a range.
Plain-text source, for agents and clipboard use: /components/source/slider.txt
When to use it
A slider sets a number where the position within its bounds matters more than the exact figure: the length of a stay, a tour duration filter, a cancellation window. It is a native range input, so arrow keys, Home and End, form submission and the slider role come from the platform. Two inputs over one track make a range, such as tours of 7 to 14 days, and the handles never cross. The value is always shown as text beside the label, with its unit, so it is never read from the handle position alone.
Examples
Value
One handle. The fill runs from the minimum to the value.
<div class="flux-slider" data-flux-slider data-flux-slider-unit="night" data-flux-slider-units="nights">
<div class="flux-field__header">
<label class="flux-field__label" for="stay-nights">Nights</label>
<span class="flux-slider__value" aria-hidden="true">4 nights</span>
</div>
<div class="flux-slider__control">
<input class="flux-slider__input" type="range" id="stay-nights" name="nights" min="1" max="14" step="1" value="4" aria-valuetext="4 nights" />
</div>
<div class="flux-slider__scale" aria-hidden="true"><span>1 night</span><span>14 nights</span></div>
</div>Duration range
Two handles on one track filter by a minimum and a maximum. The group is labelled once; each handle is named Minimum or Maximum. When both meet, the reading collapses to one value.
<div class="flux-slider" data-flux-slider data-flux-slider-unit="day" data-flux-slider-units="days" role="group" aria-labelledby="tour-length-label">
<div class="flux-field__header">
<span class="flux-field__label" id="tour-length-label">Tour length</span>
<span class="flux-slider__value" aria-hidden="true">7–14 days</span>
</div>
<div class="flux-slider__control">
<input class="flux-slider__input" type="range" name="tour-length-min" aria-label="Minimum tour length" min="1" max="21" step="1" value="7" aria-valuetext="7 days" />
<input class="flux-slider__input" type="range" name="tour-length-max" aria-label="Maximum tour length" min="1" max="21" step="1" value="14" aria-valuetext="14 days" />
</div>
<div class="flux-slider__scale" aria-hidden="true"><span>1 day</span><span>21 days</span></div>
</div>With an explanation
What the value means goes in the info bubble beside the label, never as text below the track. The input references the bubble with aria-describedby.
<div class="flux-slider" data-flux-slider data-flux-slider-unit="day" data-flux-slider-units="days">
<div class="flux-field__header">
<label class="flux-field__label" for="refund-window">Full refund window</label>
<span class="flux-info" data-flux-info>
<button class="flux-info__trigger" type="button" aria-label="About full refund window" aria-describedby="refund-window-info">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5" />
<path d="M8 7.25V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
<circle cx="8" cy="5" r="0.875" fill="currentColor" />
</svg>
</button>
<span class="flux-info__bubble" id="refund-window-info" role="tooltip" hidden>Bookings cancelled at least this many days before departure are refunded in full.</span>
</span>
<span class="flux-slider__value" aria-hidden="true">30 days</span>
</div>
<div class="flux-slider__control">
<input class="flux-slider__input" type="range" id="refund-window" name="refund-window" min="0" max="60" step="1" value="30" aria-valuetext="30 days" aria-describedby="refund-window-info" />
</div>
<div class="flux-slider__scale" aria-hidden="true"><span>0 days</span><span>60 days</span></div>
</div>Disabled
<div class="flux-slider" data-flux-slider data-flux-slider-unit="night" data-flux-slider-units="nights">
<div class="flux-field__header">
<label class="flux-field__label" for="minimum-stay">Minimum stay</label>
<span class="flux-slider__value" aria-hidden="true">2 nights</span>
</div>
<div class="flux-slider__control">
<input class="flux-slider__input" type="range" id="minimum-stay" name="minimum-stay" min="1" max="7" step="1" value="2" aria-valuetext="2 nights" disabled />
</div>
<div class="flux-slider__scale" aria-hidden="true"><span>1 night</span><span>7 nights</span></div>
</div>Anatomy
| Part | Description |
|---|---|
| Label | Visible, above the track, naming the quantity: "Tour length", not "Days". |
| Value | The current value or range with its unit, at the end of the label line. Hidden from assistive technology, which hears aria-valuetext instead. |
| Track | The rail in layer-03, and the fill in icon-brand from the start, or between the two handles of a range. |
| Handle | A ringed circle per input. It fills while focused or dragged, so a range says which handle is active. |
| Scale | The minimum and maximum, with their units, under each end of the track. |
API
| Name | Kind | Description |
|---|---|---|
flux-slider | class | The wrapper. Holds the label line, the control and the scale. |
data-flux-slider | attribute | Binds the fill, the visible value and aria-valuetext to the inputs. Requires flux-components.js. |
data-flux-slider-unit | -units | attribute | The unit for one and for every other number: "night" and "nights". Used in the value text and in aria-valuetext. |
flux-slider__value | class | The value text at the end of the label line, with aria-hidden="true". Write the initial value into it. |
flux-slider__control | class | The track. Contains one input for a value or two for a range. |
flux-slider__input | class | On each input type="range", with min, max, step, value and an initial aria-valuetext. |
flux-slider__scale | class | The minimum and maximum under the track, aria-hidden because the input already exposes its bounds. |
role="group" | attribute | On the wrapper of a range, with aria-labelledby pointing at its label. Each input then takes aria-label="Minimum …" or "Maximum …". |
States
| State | Treatment |
|---|---|
| Default | Handle ringed in icon-brand on surface; fill in icon-brand on a layer-03 rail. |
| Hover | The handle fills with surface-brand-subtle. |
| Focus | The focused handle fills in icon-brand, and keyboard focus adds the Flux focus ring around the input. |
| Dragging | Dragging focuses the input, so the handle fills in icon-brand. |
| Disabled | Handle and fill in icon-disabled; the label, value and scale turn text-disabled. |
Keyboard
| Keys | Action |
|---|---|
Arrow Right / Arrow Up | Increases the focused handle by one step. |
Arrow Left / Arrow Down | Decreases the focused handle by one step. |
Page Up / Page Down | Moves by a larger step, as the browser defines it. |
Home / End | Moves the focused handle to the minimum or maximum, or to the other handle of a range. |
Tab | Moves from the minimum handle to the maximum handle, then on. |
Accessibility
- A native input type="range" carries the slider role, its bounds and keyboard support. Do not rebuild it from a div.
- Set aria-valuetext with the unit, "7 nights" rather than "7"; flux-components.js keeps it current as the value changes.
- The visible value is aria-hidden so it is not announced twice while dragging; the input announces aria-valuetext.
- A range is a group named by its visible label, and each handle says whether it is the minimum or the maximum.
- The value is shown as text, so it is never conveyed by handle position or fill alone.
Tokens
The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.
- --flux-slider-thumb-size | -thumb-border-width | -thumb-bg | -thumb-bg-hover | -thumb-border
- --flux-slider-track-height | -track | -fill
- --flux-slider-value | -scale | -scale-gap
Do and don’t
Do
- Name the quantity in the label and the unit in data-flux-slider-unit, so every reading says what it measures.
- Choose a step that matches the decision: whole nights, whole days, five-minute intervals.
- Write the initial value and aria-valuetext into the markup, so the page reads correctly before the script runs.
Don’t
- Do not use a slider for a value people need exactly, such as a price; use a number Input.
- Do not hide the value text and leave the handle position to say it.
- Do not explain the value in text below the track. Use the info bubble.
- Do not put two independent sliders on one track. A shared track is only for a minimum and maximum of the same quantity.