# Slider

> A native range input, styled to match the other controls.

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

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

## Range and step

```javascript
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.

```javascript
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.

`src/main.js`

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

setSlider('volume', 50)
```

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

`Anywhere`

```javascript
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.

```javascript
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

```javascript
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

```javascript
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.

```javascript
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

```javascript
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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `min` | `number \| string` | `0` | Lower bound. |
| `max` | `number \| string` | `100` | Upper bound. |
| `step` | `number \| string` | — | Increment. Omit for the browser default of 1. |
| `value` | `number \| string` | — | Initial value. |
| `showValue` | `boolean` | `false` | Adds an <output> with the build-time value. |
| `color` | `'primary' \| 'neutral' \| 'success' \| 'warning' \| 'danger'` | `'primary'` | Thumb colour. |
| `name` | `string` | — | Form field name; the id is derived from it. |
| `disabled` | `boolean` | `false` | Disables the control. |

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