v1.0.0

<bmx-slider>

A value chosen along a track, or a range chosen between two of them. Ticks, a value bubble, four combinations of orientation and writing direction, and a keyboard that does everything the pointer does.

24 properties · 2 events · 3 methods · 8 parts

Example

Show markup
<div class="row">
  <bmx-slider label="Volume" value="60" ticks="10" show-value="auto" description="Arrow keys step, Page Up and Down take a tenth of the range, Home and End go to the ends."></bmx-slider>
</div>
<div class="row">
  <bmx-slider id="ex-slider-price" label="Price range" range="true" min="0" max="5000" step="50" min-distance="250" show-value="always" description="Two role=&quot;slider&quot; elements whose announced bounds are each other, so the thumbs cannot cross."></bmx-slider>
</div>
<div class="row">
  <bmx-slider label="Rating" value="3" min="1" max="5" step="1" ticks="1" tick-labels="true" size="lg" tone="warning"></bmx-slider>
</div>
<script type="module">
  await customElements.whenDefined('bmx-slider');

  const price = document.getElementById('ex-slider-price');

  // `values` and `format` are both properties: an array and a function, neither
  // of which fits in an attribute.
  price.values = [1000, 3500];
  price.format = value => `£${value.toLocaleString('en-GB')}`;
</script>

WHY THIS ONE IS NOT A NATIVE <input type="range">

§8 says real elements rather than ARIA impersonations, and bmx-checkbox and bmx-switch both follow it. This component does not, and the reason is the range.

There is no native two-ended slider. The usual answer is two <input type="range"> elements overlaid, and it is a bad one: when both thumbs sit on the same value the one underneath cannot be grabbed at all, so the control has a state it cannot get out of, and the fix is juggling pointer-events by which half of the track the pointer is in. Shipping the single-value case on a native input and the range on something else would then mean two keyboards, two sets of styling hooks and two sets of bugs in one tag.

So both are role="slider" elements, which is the pattern WAI-ARIA documents for exactly this. What that costs is the keyboard and the drag, which the platform would otherwise have given us - so the keyboard is valueForKey in src/core/slider.ts with its own tests, and the drag uses pointer capture, which is the part only a browser can prove.

A RANGE IS TWO SLIDERS

Not one slider with two values. Each thumb is its own role="slider" with its own aria-valuenow, and their aria-valuemin and aria-valuemax bound each other - so a screen reader user arrowing the lower thumb is told where it may go, which is the information a single element with two values has nowhere to put.

The thumbs do not cross. The reasoning is in boundThumb: a values[0] that means the low end at one moment and the high end at the next hands a consumer reading it mid-drag a value belonging to the other thumb.

aria-valuetext MATTERS AS MUCH AS aria-valuenow

A price slider announcing "1200" is telling the user a number, not a price, and a rating slider announcing "3" is not saying "3 stars". format supplies the text, and it is used for the bubble and the announcement together so the two can never disagree.

Properties

PropertyAttributeTypeDefaultDescription
autoFocus auto-focus boolean false Focus the first thumb once it has rendered.
description description string Help text below the track.
disabled disabled boolean false Disable the slider.
errorText error-text string An error supplied by the consumer.
format property only BmxSliderFormatter Turns a value into what the user reads. Used for the bubble, the tick labels and aria-valuetext together, so the three can never disagree - a price slider that shows "£1,200" and announces "1200" is announcing a number rather than a price.
fullWidth full-width boolean false Stretch to the width of the container.
hideLabel hide-label boolean false Hide the label visually while keeping it as the accessible name.
label label string The field's label. Required unless the label slot is used.
largeStep large-step number How far Page Up and Page Down travel. Defaults to a tenth of the range.
max max number 100 The highest value.
min min number 0 The lowest value.
minDistance min-distance number 0 The smallest gap the two thumbs may be apart.
name name string The field's name in the form it belongs to.
orientation orientation BmxOrientation 'horizontal' Which way the track runs. A vertical track runs upwards.
range range boolean false Two thumbs rather than one.
readonly readonly boolean false Make the slider read-only. It still submits and is still focusable.
showValue show-value BmxSliderValueDisplay 'auto' When the value is shown above the thumb.
size size BmxSize 'md' Size step. Scales the track, the thumbs and the text together.
step step number 1 The interval between reachable values, counted from min.
tickLabels tick-labels boolean false Label the ticks with their values.
ticks ticks number 0 Draw a tick every N steps. 0 draws none. A tick per step on a long scale is thousands of elements nobody can see, so anything over a hundred draws none either.
tone tone BmxTone 'primary' Semantic colour role for the fill, the thumbs and the focus ring.
value value number 0 The value, in single mode. Two-way: the component writes back to it.
values property only number[] [] The two ends, in range mode. Two-way. Accepts a comma-separated or JSON string as well, because an attribute is the only channel some templates have. See src/core/markup.ts.

Events

EventDetailDescription
bmxChange BmxSliderChangeDetail Fired when a movement finishes - on pointer release, or on each key press.
bmxInput BmxSliderChangeDetail Fired continuously while a thumb moves.

Methods

MethodSignatureDescription
getValues getValues() => Promise<number[]> Every thumb's value, low to high.
removeFocus removeFocus() => Promise<void> Remove focus from whichever thumb has it.
setFocus setFocus(index?: number, options?: FocusOptions) => Promise<void> Focus a thumb. The first one, unless another is named.

Slots

SlotDescription
(default) The default slot
description Rich help text, in place of the description property.
label Rich label content, in place of the label property.

CSS shadow parts

PartDescription
bubble The value shown above the thumb.
description The help text.
fill The part of the track between the ends of the selection.
label The label element.
thumb A draggable thumb.
tick One tick mark.
tick-label One tick's text.
track The full-length track.

CSS custom properties

PropertyDescription
--bmx-slider-duration How long the thumb takes to settle after a keystroke. Zero while dragging, and under reduced motion.
--bmx-slider-fill-color The selected part. Defaults to the tone's solid colour.
--bmx-slider-font-size The tick labels and the value bubble.
--bmx-slider-length A vertical slider's length. A horizontal one is sized by inline-size on the host.
--bmx-slider-radius Corner radius of the track and the fill.
--bmx-slider-thumb-border-color The thumb's ring. Defaults to the tone's solid colour.
--bmx-slider-thumb-border-width The ring around the thumb.
--bmx-slider-thumb-color The thumb's fill.
--bmx-slider-thumb-shadow The thumb's shadow, which is what lifts it off the track.
--bmx-slider-thumb-size The thumb's diameter.
--bmx-slider-tick-color The tick marks.
--bmx-slider-track-color The unfilled part of the track.
--bmx-slider-track-size The track's thickness.