# Slider

A value chosen along a range.

- Group: Forms
- Import: `import { Slider } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/slider/
- Related: progress

## Anatomy

- **Track** (required) — A 1px rule in --stone that thickens to 1.5 when the pointer is anywhere over the control, not only over the thumb.
- **Range** — The --accent fill from the minimum to the thumb, or between the two thumbs of a range.
- **Thumb** — One per entry in the value array — so the number of thumbs comes from the value, not from a prop, and a slider given neither value nor defaultValue falls back to the primitive’s own default of one thumb at the minimum. Each is a 16px circle with an invisible 44px hit area from a before pseudo-element.
- **Value readout** — Only with showValue: a mono row above the track, the names on the start edge and the formatted values on the end, each joined by an en dash and in the thumbs’ own order — so a two-ended range reads “Minimum – Maximum” over “10 – 90”.
- **Editable readout** — What editable turns those figures into: one box per thumb, showing format’s output at rest and the bare number while it has focus, so a reader still sees “$1,200” and a typist is never asked to type a currency symbol back. Each is named separately from its thumb — two controls announcing “Quality” is one control announced twice.

## Best practices

### Do

- Pass defaultValue or value whenever there is more than one end to it. The thumb count comes from that array, so a price filter left to the default is a single thumb sitting at the minimum.
- Pass an array of two names for a two-thumb range: every thumb after the first falls back to names[0], so both ends of a price filter otherwise announce themselves as “Minimum”.
- Turn on editable when the exact number matters. A slider on its own cannot be typed into, and someone who needs 37 rather than roughly 40 is dragging a 16px thumb across a hundred steps to get it — the box in the readout is the way out, and it replaces the second Input this used to ask for.

### Don’t

- format becomes each thumb’s aria-valuetext, which REPLACES the number rather than decorating it — so a formatter that rounds hard or drops the unit is what a screen reader gets instead of the value.
- editable typing is bounded by the NEIGHBOURING thumb as well as by min and max, and it has to be: 90 typed into the lower end of a range sitting at 70 would otherwise cross the two thumbs over. So a number can be accepted and then land somewhere else, and the box shows where it landed.
- Do not disable a slider to make it read-only: the whole control dims and stops taking the pointer, and Radix drops the thumb out of the tab order, so the value becomes unreachable rather than uneditable.
- Do not pass two names to a one-thumb slider: the heading prints one name per THUMB, so the second is drawn nowhere and announced nowhere.
- A Field’s label above it does not name it and does not click through: the role is on the THUMB and the root is a <span>, so the label prop here is the only name a reader hears. The hint and the error do reach the thumb.

## Accessibility

- label is required. A thumb that announces "42" and nothing else leaves a screen reader user with a number and no idea what it measures.
- A 44px hit area sits invisibly around the 16px thumb.
- format is announced as aria-valuetext, so a thumb showing “$1,200” says that rather than 1200.
- Arrows step, Page keys jump, Home and End reach the ends.

## Keyboard

- ← / → — Moves by one step.
- Page Up / Page Down — Moves by a larger step.
- Home / End — Jumps to the minimum or maximum.

## Slider

A value chosen along a range. Radix owns the keyboard contract — arrows step, Page keys jump, Home and End reach the ends — and the ARIA that reports the value. What is here is the look, and the labelling, which is the part a slider most often gets wrong: a thumb that announces "42" and nothing else leaves a screen reader user with a number and no idea what it measures. A 44px hit area sits invisibly around the 16px thumb, because a thumb sized for the design is well under any pointer-target guideline. `editable` is the answer to the thing a slider cannot do. Reach for it whenever the exact figure is the point — a budget, a timeout, a price — and leave it off when the value is genuinely approximate, because a box invites precision the setting may not have. Inside a `Field`, the hint, the error and the requirement land on the THUMB, which is the element carrying `role="slider"` — on the root they would sit on a `<span>` with no role and announce nothing. The NAME still comes from `label` here: a field's label above a slider points at that same roleless root, so it neither names the control nor clicks through to it.

### Props

- `label` (required) — `string | [string, string]`. Names the control. Required: a slider with no name announces only a number, and a number with no noun is not information. A range slider (two thumbs) needs one name per thumb — pass an array.
- `showValue` — `boolean` default `false`. Prints the current value beside the label. A two-thumb range prints both names and both values, in the same order — the heading used to print the first name over a pair of numbers, which read as "Minimum" over "10 – 90".
- `editable` — `boolean` default `false`. Turns that readout into a box the number can be typed into, and implies `showValue`. A slider is a control for a NEIGHBOURHOOD; someone who needs 1,150 rather than roughly 1,200 is dragging a 16px thumb across a hundred steps to get it. This is the way out, in the place the value already is, rather than a second field beside the track that has to be kept in step by hand. The box shows `format`'s output at rest and the bare number while it has focus, so a reader still sees "$1,200" and a typist is never asked to type a currency symbol back.
- `format` — `(value: number) => string`. Renders the value with a unit or a currency, e.g. `(n) => n + '%'`. Reaches assistive tech as well as the readout: it becomes each thumb's `aria-valuetext`, so a thumb showing "$1,200" announces that rather than the bare number. Left off, the platform announces the value itself.

Also accepts: `ComponentProps<typeof SliderPrimitive.Root>`.

## Example — default

```tsx
import { Slider } from '@misoto22/folio'

<div className="flex w-full max-w-sm flex-col gap-8">
  <Slider label="Quality" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />
  <Slider
    label={['Minimum price', 'Maximum price']}
    defaultValue={[20, 70]}
    max={100}
    step={5}
    showValue
    format={(n) => `$${n}`}
  />
</div>
```

## Example — typing the exact value

```tsx
import { Slider } from '@misoto22/folio'

<div className="flex w-full max-w-sm flex-col gap-8">
  <Slider
    label="Monthly budget"
    defaultValue={[1200]}
    max={5000}
    step={50}
    editable
    format={(n) => `$${n}`}
  />
  <Slider
    label={['Minimum price', 'Maximum price']}
    defaultValue={[20, 70]}
    max={100}
    step={5}
    editable
    format={(n) => `$${n}`}
  />
</div>
```

## Example — naming the unit

```tsx
import { Slider } from '@misoto22/folio'

<div className="flex w-full max-w-sm flex-col gap-8">
  <Slider
    label="Request timeout in seconds"
    defaultValue={[12]}
    min={1}
    max={60}
    showValue
    format={(n) => `${n}s`}
  />
  <Slider label="Retries before giving up" defaultValue={[3]} max={10} showValue />
</div>
```
