Slider

An <input type="range">.

import { Slider } from '@apostel/bedrock'
Loading demo…
Source
import { useState } from 'react'
import { Slider } from '../../src/index'

/**
 * `<input type="range">`. Arrow keys, Home/End, Page Up/Down and the drag
 * behaviour are the element's, along with the correct `role="slider"` and the
 * announced value — none of which is written here.
 */
export default function SliderDemo() {
  const [value, setValue] = useState(40)

  return (
    <>
      <Slider.Root
        aria-label="Volume"
        defaultValue={40}
        min={0}
        max={100}
        step={5}
        onValueChange={setValue}
      >
        <Slider.Track>
          <Slider.Range />
        </Slider.Track>
        <Slider.Thumb />
      </Slider.Root>
      <output>{value}</output>
    </>
  )
}

Anatomy

<Slider.Root>
  <Slider.Track>
    <Slider.Range />
  </Slider.Track>
  <Slider.Thumb />
</Slider.Root>

Track, Range and Thumb all render nothing. An <input> is a void element and cannot have children, so they exist only so Radix-shaped markup compiles and keeps its names. They accept and drop children, and warn in development if given a className.

If you are writing new markup, <Slider.Root /> on its own is the whole component.

Slider.Root

Renders <input type="range" data-bedrock-slider>. Takes every <input> prop except the ones it owns.

prop type notes
value number Controlled.
defaultValue number Uncontrolled.
min / max number The element's own.
step number The element's own.
orientation 'horizontal' | 'vertical' Becomes writing-mode.
onValueChange (value: number) => void Alongside onChange.
asChild boolean The child must render an <input>.

role="slider", aria-valuenow, aria-valuemin, aria-valuemax, the announced value, the drag behaviour and the full keyboard are the element's. None of it is written here.

Styling

There are no child elements to style, so use the pseudo-elements:

[data-bedrock-slider] { appearance: none }
[data-bedrock-slider]::-webkit-slider-runnable-track { height: 4px; background: #ddd }
[data-bedrock-slider]::-webkit-slider-thumb { appearance: none; width: 16px; height: 16px }
[data-bedrock-slider]::-moz-range-track { height: 4px; background: #ddd }
[data-bedrock-slider]::-moz-range-thumb { width: 16px; height: 16px }

A filled "range" up to the thumb has no interoperable pseudo-element. Use a linear-gradient background sized from the value, which is one custom property and no JavaScript.

Keyboard

key behaviour
ArrowLeft/Right One step.
ArrowUp/Down One step.
PageUp/PageDown A larger step.
Home/End Minimum and maximum.

All of it native, including the right behaviour in right-to-left writing modes.

What is not here