v1.2.0
  1. ds
  2. Components
  3. Range and slider

Range and slider

A native range in the accent, with its value and tick marks.

Range

Code
<input class="range" type="range" min="0" max="100" value="40" aria-label="Opacity" style="max-width:20rem" />

A native input type="range" with its own track and thumb: a 4px track, a 14px thumb ringed in the accent, a soft halo on hover and a ring on the thumb for keyboard focus. It is still the native control, so arrow keys, Page Up and Down, touch and screen readers all work. On touch screens the thumb grows to 18px.

The fill

Firefox draws the filled part of the track by itself. Chrome and Safari cannot, so the fill comes from --v, the value as a number from 0 to 1. ds.css reads it; three lines of script keep it current.

const fill = (r) => r.style.setProperty('--v', (r.value - r.min) / (r.max - r.min));
document.querySelectorAll('input.range').forEach(fill);
addEventListener('input', (e) => e.target.matches?.('input.range') && fill(e.target));

With a label and value

1×
16 px
Code
<div class="slider" style="max-width:26rem">
  <label for="r-speed">Speed</label>
  <input id="r-speed" class="range" type="range" min="0.25" max="2" step="0.25" value="1" />
  <output for="r-speed">1×</output>
</div>
<div class="slider stack" style="max-width:18rem">
  <label for="r-pad">Padding</label>
  <input id="r-pad" class="range" type="range" min="0" max="48" step="4" value="16" />
  <output for="r-pad">16 px</output>
</div>

.slider puts the label, the range and the value in one row. .stack moves the label and value above the range, which suits narrow side panels.

Tick marks

1 h
Code
<div class="slider" style="max-width:26rem">
  <label for="r-step">Bucket</label>
  <input id="r-step" class="range" type="range" min="0" max="4" step="1" value="2" aria-valuetext="1 hour" />
  <output for="r-step">1 h</output>
  <div class="range-ticks" aria-hidden="true"><span>5m</span><span>15m</span><span class="on">1h</span><span>6h</span><span>1d</span></div>
</div>

Ticks are spread evenly and inset by half a thumb, so each one sits under the thumb's centre at its value. Give every step a tick when there are only a few. On a long range, label the ends and a few round numbers. .on marks the current tick.

API

ClassOnWhat it does
.rangeinput[type=range]Track, fill and thumb. Works with min, max and step as usual.
--v.range or a parentThe value from 0 to 1, for the fill in Chrome and Safari.
--range-thumb / --range-track.range and its ticksThumb size (0.875rem) and track height (0.25rem). Set them on a parent so ticks follow.
.sliderdivLabel, range and value in a row.
.stack.sliderLabel and value above the range.
output / .val.slider > *The value: mono, tabular figures, right-aligned.
.range-ticksdivEvenly spaced ticks under a range. .on marks the current one.

Accessibility

  • Label every range with a label or aria-label. When the number alone does not say it, set aria-valuetext: "1 hour", "4.2 seconds of 12".
  • Show the value in an output tied to the input with for.
  • Ticks are decoration and carry aria-hidden; the value text carries the meaning.