# Slider

> Drag-to-pick numeric input. Single-thumb by default, optional dual-thumb range mode. A signal-forms native control — it implements `FormValueControl`, so `[formField]` binds straight to its `value` model. `[(value)]` works standalone, and template-driven `[(ngModel)]` / reactive forms keep working through [Angular 22's forms bridge](https://ngwr.dev/guides/forms).

Source: https://ngwr.dev/reference/components/slider  
Kind: Signal Forms, a11y

## Installation

```angular-ts
import { WrSlider } from 'ngwr/slider';

@Component({ imports: [WrSlider, FormsModule] })
export class MyComponent {
  protected volume = 35;
}
```

## Single value

The default. Bind a `number` through any form mechanism.

```angular-html
<wr-slider [(value)]="volume" min="0" max="100" />
```

```html
<wr-slider [(value)]='volume' min='0' max='100' />
```

## Range

Set `range` to render two thumbs. The bound value is `[low, high]`.

```angular-html
<wr-slider [(value)]="priceRange" range min="0" max="1000" step="50" />
```

```html
<wr-slider [(value)]='priceRange' range min='0' max='1000' step='50' />
```

## Step

The thumb snaps to the nearest multiple of `step`.

```html
<wr-slider [(value)]='stepped' min='0' max='100' step='25' />
```

## Disabled

```html
<wr-slider [(value)]='volume' disabled />
```

## Keyboard

The thumb is a real `<button>` with `role='slider'` — full keyboard support out of the box. Arrow keys step by `step`; Shift + arrow or PageUp / PageDown step by `step × 10`; Home / End jump to min / max.

## Values outside the range, and `null`

`[min]` and `[max]` bound what the thumb can produce and where it can be drawn. A value written in from a form is clamped for the **display** only — the model keeps exactly what you set.

Set `155` on a slider bound with `max="100"` and the thumb parks at the top of its track and announces `aria-valuenow="100"`, while `control.value` stays `155`. The asymmetry is deliberate and matches `wr-rating` and `wr-input-number`: a thumb cannot render off its own track, so the display has no choice — but writing that clamp back into the model would delete the out-of-range value a `Validators.max` exists to report, and would mark a pristine form dirty on first paint. The first drag or arrow key commits an in-range number and the two agree from then on.

**`WrSliderValue` is `number | [number, number]` — there is no empty state.** A slider always points somewhere, so a value that is neither shape is ignored and the thumb stays where it was: `control.reset()`, which resets to `null` by default, leaves the thumb on the old position. Reset to a number instead — `control.reset(0)`, or whatever your `min` is. In `range` mode, a scalar written to a two-thumb slider seeds the high thumb at `max` and, again, leaves the model holding the scalar you gave it until a thumb moves.

[The reactive-forms guide](https://ngwr.dev/guides/forms) has the same table for every value control, next to the two bridge behaviours that surprise people more: `updateOn` does not apply, and a `{ emitEvent: false }` write does not repaint.

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `min` | Lower bound. | `WrSliderValue \| undefined` | `0` |
| `max` | Upper bound. | `WrSliderValue \| undefined` | `100` |
| `step` | Step size for keyboard and drag. | `number` | `1` |
| `range` | Render two thumbs and emit `[low, high]`. | `boolean` | `false` |
| `disabled` | Disable interaction. Bound automatically from the field's disabled state when used with `[formField]`. | `boolean` | `false` |
| `readonly` | Refuse value changes while the thumbs stay focusable and the value still submits. Bound automatically from the field's readonly state when used with `[formField]`. The thumbs keep their tab stop and keep announcing their value — arrow keys, Home / End and the pointer simply move nothing — which is the whole difference from `disabled`, where they would leave the tab order entirely. Mirrored as `aria-readonly`, which role `slider` supports. | `boolean` | `false` |
| `showLabel` | Render the current value below the track. | `boolean` | `true` |
| `ariaLabel` | Accessible name of the thumb — the single one, or the LOWER one in range mode. Falls back to `slider.label` / `slider.lower`. | `string \| null` | `null` |
| `upperLabel` | Accessible name of the upper thumb in range mode. Falls back to `slider.upper`. | `string \| null` | `null` |
| `value` | Current value. Bound by `[formField]`, or two-way via `[(value)]`. Shape follows `range`: a plain `number`, or `[low, high]` in range mode. | `WrSliderValue` | `0` |
| `(touch)` | Emitted on blur so a bound field can mark itself touched. | `void` | — |

## CSS variables

Custom properties `ngwr/slider` publishes. Each default below is declared on the component's own selector, so a `:root` override is shadowed by it — set them on that selector, on a wrapper you scope yourself, or inline on the element. Unlike the BEM class names, these are the supported way to restyle the component.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-slider-fill` | `var(--wr-color-primary)` | `.wr-slider` +1 variant override |
| `--wr-slider-label` | `var(--wr-color-on-surface-muted)` | `.wr-slider` |
| `--wr-slider-thumb` | `var(--wr-color-surface)` | `.wr-slider` |
| `--wr-slider-thumb-border` | `var(--wr-color-primary)` | `.wr-slider` +1 variant override |
| `--wr-slider-thumb-size` | `1rem` | `.wr-slider` |
| `--wr-slider-track` | `var(--wr-color-outline)` | `.wr-slider` |
| `--wr-slider-track-height` | `0.25rem` | `.wr-slider` |
