Slider

This is a real <input type="range"> — arrow keys, Home and End, and the correct announcement all come from the browser. Only the track and thumb are styled.

Basic slider

sliderField({ label: 'Quality', name: 'quality', value: 70 })

Range and step

stack({ gap: 'lg' },
  sliderField({ label: 'Volume', name: 'volume', min: 0, max: 100, value: 40 }),
  sliderField({ label: 'Columns', name: 'columns', min: 1, max: 6, step: 1, value: 3 }),
  sliderField({ label: 'Scale', name: 'scale', min: 0.5, max: 2, step: 0.25, value: 1 }),
)

Showing the value

showValue puts an <output> beside the track carrying the value the page was built with, and the input fetches its own handler on the first drag, so the number follows the thumb. There is nothing to import: a value that silently went stale would be worse than no value at all, so this one is not left to you.

82
Higher is larger and slower to build.
sliderField({
  label: 'Image quality',
  name: 'jpeg-quality',
  min: 40,
  max: 100,
  value: 82,
  showValue: true,
  help: 'Higher is larger and slower to build.',
})

Setting it from your own code

A slider is a form control, so the reader owns it — but a preset, a reset button, or a value arriving over the network still has to be able to move it. Give it an id and setSlider does, taking the <output> with it.

import { setSlider } from 'sitelo/ui/client'

setSlider('volume', 50)

Or reach the module the way the components reach theirs, and skip the bundle entirely:

button({ onclick: "import('/su/slider.js').then(m=>m.set('volume',50))" }, 'Half')

The browser clamps to min and max and snaps to step, so what comes back is where the slider landed rather than what it was handed. input and change follow, because a preview listening for the drag has no other way to hear a move it did not cause. getSlider() reads the value back.

Try it

This page loads the runtime, so the buttons below really do move the slider.

40
stack({ gap: 'md' },
  slider({ id: 'demo-slider', value: 40, showValue: true, 'aria-label': 'Demo' }),
  stack({ direction: 'row', gap: 'sm', wrap: true },
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/slider.js').then(m=>m.set('demo-slider',0))" }, 'Min'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/slider.js').then(m=>m.set('demo-slider',50))" }, 'Half'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/slider.js').then(m=>m.set('demo-slider',100))" }, 'Max'),
  ),
)

Colors

stack({ gap: 'lg' },
  slider({ value: 70, color: 'primary', 'aria-label': 'Primary' }),
  slider({ value: 55, color: 'success', 'aria-label': 'Success' }),
  slider({ value: 35, color: 'warning', 'aria-label': 'Warning' }),
  slider({ value: 20, color: 'danger', 'aria-label': 'Danger' }),
)

Disabled

sliderField({ label: 'Locked', name: 'locked', value: 50, disabled: true })

Without a label

A bare slider() is the control on its own — give it an aria-label when there is no visible label to point at it.

Aa

Aa

stack({ direction: 'row', gap: 'md', align: 'center' },
  text({ variant: 'small', tone: 'muted' }, 'Aa'),
  slider({ min: 12, max: 24, value: 16, 'aria-label': 'Text size' }),
  text({ tone: 'muted' }, 'Aa'),
)

In a form

1280
82
card(
  cardBody(
    stack({ gap: 'lg' },
      sliderField({ label: 'Max image width', name: 'max-width', min: 640, max: 2560, step: 160, value: 1280, showValue: true }),
      sliderField({ label: 'Quality', name: 'q', min: 40, max: 100, value: 82, showValue: true }),
    ),
  ),
  cardFooter({ divided: true, style: 'justify-content: flex-end' },
    button({ type: 'submit' }, 'Save'),
  ),
)

Props

PropTypeDefaultDescription
minnumber | string0Lower bound.
maxnumber | string100Upper bound.
stepnumber | string—Increment. Omit for the browser default of 1.
valuenumber | string—Initial value.
showValuebooleanfalseAdds an <output> with the build-time value.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Thumb colour.
namestring—Form field name; the id is derived from it.
disabledbooleanfalseDisables the control.

sliderField() additionally takes label, help, error and required — see textField().