# Slider

> A track with a thumb people drag or move with arrow keys to pick a number, or two thumbs for a range, with a label, tones, and sizes.

- Name: `slider`
- Status: beta
- Categories: forms
- File: the install writes `slider.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`, `class-variance-authority`. The install adds them.
- Gallery: https://txbrendan.dev/components/slider
- Registry item: https://txbrendan.dev/r/slider.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@txbrendan": "https://txbrendan.dev/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@txbrendan` in a public list that txbrendan/ui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @txbrendan/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @txbrendan/slider
   ```

## Use it for

- Picking a number where the rough amount matters more than the exact one, like volume or brightness.
- A range with two thumbs, like a price filter. Pass an array: defaultValue={\[20, 80\]}.
- Give it a label, and add showValue when people need the number. Use format for units, like dollars.
- Name each thumb of a range with thumbLabels, like \['Lowest price', 'Highest price'\].

## Avoid it for

- An exact number, like a quantity or an age. Use an input with type='number'.
- A choice from a few named options. Use a radio group.
- A setting that is on or off. Use a switch.
- A value that only shows progress. Use a progress bar.

## Props

- `label`: `ReactNode`. Visible text above the track. Without it, TypeScript requires aria-label or aria-labelledby.
- `aria-label`: `string`. Names the slider when there is no visible label. It goes on each thumb.
- `aria-labelledby`: `string`. The id of visible text elsewhere that names the slider. It goes on each thumb.
- `showValue`: `boolean`. Default `false`. Shows the value as text at the end of the label row. A range shows both ends, like '20 – 80'.
- `thumbLabels`: `readonly string[]`. A name for each thumb of a range slider, in order. It replaces the label as each thumb's name.
- `tone`: `"neutral" | "brand" | "danger" | "success" | "warning"`. Default `"neutral"`. Color of the filled part and the thumb's ring.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Track and thumb size, and label text size. The row is as tall as a button of the same size.
- `value`: `number | number[]`. Controlled value. An array makes a range, with one thumb per value. Use it with onValueChange.
- `defaultValue`: `number | number[]`. Starting value when you do not control it. An array makes a range.
- `onValueChange`: `(value: number | number[], details) => void`. Called on every move, while dragging too.
- `onValueCommitted`: `(value: number | number[], details) => void`. Called once when a drag or key press ends. Save here, not in onValueChange.
- `min`: `number`. Default `0`. The lowest value.
- `max`: `number`. Default `100`. The highest value.
- `step`: `number`. Default `1`. How far one arrow key press moves. Values snap to it.
- `largeStep`: `number`. Default `10`. How far Page Up, Page Down, or Shift with an arrow moves.
- `minStepsBetweenValues`: `number`. Default `0`. For a range, the fewest steps the two thumbs can be apart.
- `format`: `Intl.NumberFormatOptions`. How the value reads, as text and to screen readers, like { style: 'currency', currency: 'USD' }.
- `orientation`: `"horizontal" | "vertical"`. Default `"horizontal"`. Vertical sliders are 10rem tall. className='h-64' changes it.
- `disabled`: `boolean`. Default `false`. Blocks moving it and removes it from the tab order. It dims.
- `name`: `string`. Form field name. The value is sent with the form.
- `className`: `string`. Classes for the root. Later classes win, so className='max-w-xs' narrows it.

## Accessibility

- Each thumb holds a native input with type="range", so it has role="slider" and the value is sent with the form.
- Arrow keys move by step. Page Up and Page Down, or Shift with an arrow, move by largeStep. Home and End go to min and max.
- Each thumb is a tab stop. Tab moves from the first thumb of a range to the second.
- TypeScript requires a visible label, aria-label, or aria-labelledby, so each thumb has a name.
- In server HTML, only aria-label, aria-labelledby, and thumbLabels name a thumb. The visible label is linked once JavaScript runs.
- A range's thumbs read as 'start range' and 'end range' by default. Name them with thumbLabels.
- The focus ring sits on the thumb while its input has keyboard focus. It uses --ring, which the theme tests hold to 3:1.
- The whole row, as tall as a button, takes clicks and touches, not just the thin track.
- The fill uses the tone's color on the muted track. No contrast pair checks the fill or the track, so the label and value carry the meaning.

## Examples

### With a label and value

```tsx
<Slider label="Volume" defaultValue={60} showValue tone="brand" />
```

### Price range

```tsx
<Slider
  label="Price"
  defaultValue={[20, 80]}
  max={200}
  step={5}
  minStepsBetweenValues={2}
  showValue
  format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
  thumbLabels={["Lowest price", "Highest price"]}
/>
```

### No visible label

```tsx
<Slider aria-label="Brightness" defaultValue={40} size="sm" className="max-w-48" />
```

### Save when the drag ends

```tsx
"use client";
import { useState } from "react";

function TextSize({ save }: { save: (size: number) => void }) {
  const [size, setSize] = useState(16);
  return (
    <Slider
      label="Text size"
      showValue
      min={12}
      max={24}
      value={size}
      onValueChange={(value) => setSize(value as number)}
      onValueCommitted={(value) => save(value as number)}
    />
  );
}
```
