Slider
Sets a number or a range by dragging along a track.
Examples
Parallel conversations
Alert when daily spend leaves
Value
Review sampling
one valuedefault
Alert when daily spend leaves
range
States
rest
hover
dragging
focus-visible
disabled
Usage
When to use it
- Use
- The value sits on a known scale and the person thinks in more or less: sampling rate, concurrency, a spend band.
- Use
- The person wants to see the value against the whole range while setting it.
- Not for
- The exact number matters, like a refund amount or a timeout in seconds. Use Input.
- Not for
- There are fewer than five meaningful positions. Use Segmented control.
Rules
8 rules
- Must
- Save in onValueCommitted, not onValueChange. Because onValueChange fires on every pixel of a drag, so a save there sends dozens of requests and can land out of order.
- Should
- Pass showValue and a format when the value means something on its own.
- Must
- Put the unit in format, not in the label. Because format also writes aria-valuetext, so without it a screen reader reads "0.25" where the eye sees "25%".
- Must
- Pass thumbLabels on a two-thumb range. Because both thumbs otherwise share the one name, and a screen reader cannot tell the lower bound from the upper.
- Must
- Name a single slider with aria-label or aria-labelledby. Because the thumb is a hidden input with no label of its own, so a screen reader reads only "slider".
- Must
- Give the track at least 160px. Because below that one step on 0 to 100 is under 2px and the thumb cannot land on a chosen value.
- Never
- Fill the track with a hue or gradient; the range is ink on the row rule. Because a hue on the range reads as a status, good or bad, that the value does not have.
- Never
- Render a slider whose min equals max. Because the thumb cannot move, so the person drags a control that changes nothing.
Accessibility
5 checks
- Each thumb is a slider input with aria-valuenow, aria-valuemin, aria-valuemax and aria-valuetext from format.
- Arrow keys move by step, mirrored in RTL; Page Up and Page Down move by largeStep; Home and End jump.
- Tab moves to the second thumb of a range.
- The read-out is an output that announces politely; its width stand-ins are aria-hidden.
- The control is touch-none so a horizontal drag moves the thumb and a vertical swipe scrolls.
Edge cases
9 edge cases
- Long read-out in 320px
- The read-out takes its widest width and the track shrinks.
- Container 120px
- The track shrinks beside the read-out and never overflows.
- Right to left
- The range fills from the right and the arrows follow.
- Zoom 200%
- The read-out doubles and the track shrinks; the thumb stays 16px.
- Held arrow key
- One step per repeat, stopping at max; the read-out reserves the widest of min, max and one step in from each.
- On the inverse surface
- Ink and paper flip.
- Failed save
- Set value back; the thumb jumps to it.
- Forced colors
- Track has a CanvasText border, range is Highlight, thumb is CanvasText.
- Reduced motion
- The thumb never grows; hover and drag only step its ink.
API
8 props
value / defaultValuenumber | number[]defaultminonValueChange / onValueCommitted(value, details) => voidmin, max, step, largeStepnumberdefault0, 100, 1, 10formatIntl.NumberFormatOptionslocaleIntl.LocalesArgumentdefault'en-US'showValuebooleandefaultfalsethumbLabelsstring[]orientation, disabled, nameBase UIdefault'horizontal'
Tokens
13 tokens
--line-row- oklch(var(--ink-c) / 0.08)
--line-control- oklch(var(--ink-c) / 0.16)
--radius-soft- 6px
--ink- oklch(var(--ink-c))
--paper- oklch(var(--paper-c))
--text-disabled- oklch(var(--ink-c) / 0.40)
--surface-disabled- color-mix(in oklch, var(--paper), var(--ink) 8%)
--line-focus- 2px
--border-focus- var(--ink)
--focus-offset- 2px
--type-label- 400 14px/16px var(--font-sans)
--ring-thumb- 0 0 0 2px var(--paper)
--button-primary-hover- oklch(var(--ink-c) / 0.86)