<!-- Protocore Design System — Slider -->
# Slider

- **Category:** Inputs (`inputs`)
- **Slug:** `inputs/slider`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Slider } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/inputs/slider

> Range control with a hairline track, ink fill, and square thumbs — single value or a min/max range.

## When to use it

Use a **Slider** for an **approximate value on a bounded, continuous range** where dragging feels natural and the exact number matters less than the relative position — a throttle, a threshold, an autoscale band. When users need to type or read a precise figure, pair it with (or replace it with) a **NumberInput**. For a handful of discrete named choices, a **SegmentedControl** or **RadioGroup** is clearer than a stepped slider. Always surface the current value in nearby text — the thumb position alone isn't a readout.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | no | — | — |
| `className` | `string` | no | — | — |
| `defaultValue` | `SliderValue` | no | — | Initial value(s) in uncontrolled mode. |
| `marks` | `number[]` | no | — | Optional tick marks drawn on the track at these raw values. |
| `max` | `number` | no | `100` | Maximum bound. |
| `min` | `number` | no | `0` | Minimum bound. |
| `onValueChange` | `((value: SliderValue) => void)` | no | — | Fires with the next value(s), matching the shape (`number` vs `number[]`) you supplied. |
| `step` | `number` | no | `1` | Step increment. |
| `style` | `CSSProperties` | no | — | — |
| `value` | `SliderValue` | no | — | Controlled value(s). A single `number` renders one thumb; a `number[]` renders one thumb per entry. |

## Examples

### Basics

Pass a single `number` for one thumb. `onValueChange` echoes back the shape you supplied — a plain `number` here.

```tsx
import { useState } from "react";
import { Slider, Text } from "@protocore/pds";

export default function Demo() {
  const [cpu, setCpu] = useState(60);

  return (
    <div style={{ display: "grid", gap: 12, width: 320 }}>
      <Text size="sm" color="muted">
        CPU throttle: {cpu}%
      </Text>
      <Slider value={cpu} onValueChange={(v) => setCpu(v as number)} aria-label="CPU throttle" />
    </div>
  );
}
```

### Range

Pass a `number[]` and the Slider renders one thumb per entry, giving you a min/max range. `onValueChange` returns an array.

```tsx
import { useState } from "react";
import { Slider, Text } from "@protocore/pds";

export default function Demo() {
  const [range, setRange] = useState<number[]>([20, 80]);

  return (
    <div style={{ display: "grid", gap: 12, width: 320 }}>
      <Text size="sm" color="muted">
        Autoscale between {range[0]} and {range[1]} replicas
      </Text>
      <Slider
        value={range}
        onValueChange={(v) => setRange(v as number[])}
        min={0}
        max={100}
        step={5}
      />
    </div>
  );
}
```

## Do & don't

**Do**

- Show the live value(s) in adjacent text — the track is not a precise readout.
- Give the control an accessible name via `aria-label` or a wrapping `Field`.
- Use a `number[]` value for min/max ranges and a bare `number` for single thumbs.
- Set a sensible `step` so keyboard and drag land on usable increments.

**Don't**

- Don't use a slider when an exact value must be entered — use NumberInput.
- Don't use it for more than a few discrete options — use a SegmentedControl.
- Don't set so many `marks` that the track becomes visual noise.
- Don't hide the current value; position alone fails for precise or a11y use.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus to the (next) thumb. |
| `Arrow Left / Down` | Decrease the focused thumb by one step. |
| `Arrow Right / Up` | Increase the focused thumb by one step. |
| `Home / End` | Jump the focused thumb to the min / max. |
| `Page Up / Page Down` | Move by a larger increment. |

**Notes**

- Built on Radix Slider: each thumb is a `role="slider"` with `aria-valuemin`, `aria-valuemax`, and `aria-valuenow`.
- Give a clearer `aria-label` per thumb for range sliders where 'minimum' / 'maximum' aids comprehension.
- Thumbs can't cross; each is bounded by its neighbour in a multi-thumb range.

## Related

`number-input`, `field`, `switch`, `segmented-control`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
