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

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

> Two-thumb range control — a hairline track with an ink fill between a low and high value.

## When to use it

Use a **RangeSlider** for a **bounded min/max band** on a continuous scale — a price range, a latency window, an acceptable-value corridor. When users need a single threshold, use **Slider**; when they must type exact figures, pair with (or use) two **NumberInput**s; for a calendar range, use **DateRangePicker**. Always surface both live values in nearby text — thumb positions alone aren't a readout.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | no | — | — |
| `className` | `string` | no | — | — |
| `defaultValue` | `RangeValue` | no | `[min, max]` | Initial `[low, high]` value in uncontrolled mode. |
| `marks` | `number[]` | no | — | Optional tick marks drawn on the track at these raw values. |
| `max` | `number` | no | `100` | Maximum bound. |
| `maxLabel` | `string` | no | `Maximum` | Accessible label for the upper thumb. |
| `min` | `number` | no | `0` | Minimum bound. |
| `minLabel` | `string` | no | `Minimum` | Accessible label for the lower thumb. |
| `minStepsBetweenThumbs` | `number` | no | `0` | Minimum number of steps the two thumbs must keep between them. |
| `onValueChange` | `((value: RangeValue) => void)` | no | — | Fires with the next `[low, high]` pair as either thumb moves. |
| `step` | `number` | no | `1` | Step increment. |
| `style` | `CSSProperties` | no | — | — |
| `value` | `RangeValue` | no | — | Controlled `[low, high]` value. |

## Examples

### Basics

Pass a `[low, high]` tuple. Each thumb moves independently and can't cross its neighbour; `onValueChange` returns the next `[low, high]` pair.

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

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

  return (
    <div style={{ display: "grid", gap: 12, width: 340 }}>
      <Text size="sm" color="muted">
        Latency SLO band: {range[0]}–{range[1]} ms
      </Text>
      <RangeSlider
        value={range}
        onValueChange={setRange}
        min={0}
        max={800}
        step={10}
        minLabel="Lower bound"
        maxLabel="Upper bound"
      />
    </div>
  );
}
```

### Steps & marks

Constrain movement with `step`, keep a gap between thumbs with `minStepsBetweenThumbs`, and draw ticks at raw values with `marks`.

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

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

  return (
    <div style={{ display: "grid", gap: 16, width: 340 }}>
      <Text size="sm" color="muted">
        Autoscale between {range[0]}% and {range[1]}% CPU
      </Text>
      <RangeSlider
        value={range}
        onValueChange={setRange}
        min={0}
        max={100}
        step={5}
        minStepsBetweenThumbs={2}
        marks={[0, 25, 50, 75, 100]}
        minLabel="Scale-down threshold"
        maxLabel="Scale-up threshold"
      />
    </div>
  );
}
```

## Do & don't

**Do**

- Show both live values in adjacent text — the track is not a precise readout.
- Name each thumb via `minLabel` / `maxLabel`.
- Set a `step` so keyboard and drag land on usable increments.
- Use `minStepsBetweenThumbs` when a zero-width band is meaningless.

**Don't**

- Don't use it when exact values must be entered — use NumberInputs.
- Don't hide the current values; position alone fails for precise or a11y use.
- Don't set so many `marks` that the track becomes noise.
- Don't use a range when a single threshold is enough.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus to the next thumb (lower, then upper). |
| `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 its 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`.
- The lower and upper thumbs are labelled 'Minimum' and 'Maximum' by default — override with `minLabel` / `maxLabel`.
- Thumbs can't cross; each is bounded by its neighbour.

## Related

`slider`, `number-input`, `field`, `date-range-picker`

---

© 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/
