Slider
A styled native range input with a live percentage badge.
Last updated
Uses JavaScript
A small island updates the badge text and the track’s fill on every input
event, keeps the badge centred over the thumb, and in range mode stops the
two thumbs from crossing.
Without JS: The range input itself needs no JS — it's a native range input, fully draggable and keyboard-operable, and its initial coloured fill is rendered server-side from value/min/max. Only the floating value badge is JS-driven; it's hidden via no-js:hidden when scripts don't run.
Show codeHide code
<Slider aria-label="Volume" value={40} />Add
npx @ava-ui/apogee add slider
No Apogee component dependencies.
Usage
---
import Slider from '@/components/apogee/Slider/Slider.astro'
---
<Slider name="volume" min={0} max={100} value={40} aria-label="Volume" />
API
@composable — exposed to Content Editors in AVA. Any other <input> attribute
(e.g. id, aria-labelledby, data-*) passes through onto the range input.
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
— | Form field name. |
min |
number |
0 |
Minimum value. |
max |
number |
100 |
Maximum value. |
step |
number |
1 |
Step increment. |
value |
number |
midpoint of min/max |
Initial value. Clamped to min/max and snapped to step before render. |
disabled |
boolean |
false |
Disables the input. |
range |
boolean |
false |
Two thumbs. Uses minValue / maxValue and submits two fields. |
minValue / maxValue |
number |
min / max |
Initial thumb values in range mode. |
minName / maxName |
string |
<name>-min / <name>-max |
Submitted names in range mode. |
format |
string | false |
'{value}' |
Badge text with a {value} token ('${value}', '{value}%'). false hides the badge. |
orientation |
'horizontal' | 'vertical' |
'horizontal' |
Vertical turns the input; give the field a height with class. |
color |
'primary' | 'neutral' | 'critical' | 'positive' | 'warning' |
'primary' |
Hue of the filled track and thumb. |
labelPosition |
'top' | 'bottom' |
'top' |
Where the badge sits relative to a horizontal track. |
aria-label |
string |
— | Accessible name. No visible <label> is rendered — set this or associate an external <label> via id / aria-labelledby. |
class |
string |
— | Extra classes on the wrapping <div>, merged last. |
Value and format
The badge shows the value. format wraps it; false hides the badge.
Show codeHide code
<Slider min={0} max={50} value={10} format="${value}" aria-label="Budget" />
<Slider value={40} format="{value}%" aria-label="Opacity" />
<Slider value={25} format={false} aria-label="No badge" />Range
Two thumbs that never cross; submits price-min and price-max.
Show codeHide code
<Slider range name="price" min={0} max={100} step={10} minValue={20} maxValue={60} aria-label="Price" />With a visible label, aria-labelledby names both thumbs; prefer aria-label
when each thumb should be named on its own (“Budget minimum” / “Budget maximum”).
Show codeHide code
<label id="budget-label">Budget</label>
<Slider range name="budget" minValue={1000} maxValue={4000} max={5000} step={100} aria-labelledby="budget-label" />Orientation
Show codeHide code
<Slider aria-label="Volume" value={50} />
<Slider orientation="vertical" aria-label="Volume" class="h-48" />Variants
labelPosition — horizontal only:
Show codeHide code
<Slider aria-label="Top badge" labelPosition="top" value={30} />
<Slider aria-label="Bottom badge" labelPosition="bottom" value={70} />Every color:
Show codeHide code
<Slider aria-label="Primary" color="primary" value={60} />
<Slider aria-label="Neutral" color="neutral" value={60} />
<Slider aria-label="Critical" color="critical" value={60} />
<Slider aria-label="Positive" color="positive" value={60} />
<Slider aria-label="Warning" color="warning" value={60} />States
- Default — track fill matches
value(or the midpoint ifvalueis unset), rendered server-side so there’s no flash before JS loads. - Hover — pointer cursor over the track/thumb.
- Focus — visible ring on the thumb (
focus-visible). - Disabled — track and thumb take the disabled colour,
cursor-not-allowed; nativedisabledblocks interaction and removes it from the tab order. - No JS — badge hidden (
no-js:hidden); input and fill stay fully usable.
Show codeHide code
<Slider aria-label="Disabled" value={50} disabled />Examples
With a visible, associated label:
<label for="volume-slider">Volume</label>
<Slider id="volume-slider" name="volume" value={60} />
Controlled range with a fractional step:
<Slider name="rating" min={0} max={5} step={0.5} value={2.5} aria-label="Rating" />
Accessibility
| Key | Action |
|---|---|
Tab |
Moves focus to the thumb; a range slider has one stop per thumb. |
← → |
Step down / up by step; swapped under dir="rtl". |
↓ ↑ |
Step down / up by step. |
PageDown PageUp |
Step down / up by a tenth of the range, or by step if that is larger. |
Home End |
Jump to min / max. |
- No visible
<label>is rendered — always passaria-label, or associate an external<label for>withid. - The badge is
aria-hidden— it echoes the input’s value visually; screen readers announce the native input’s value/min/max, not the badge text. - Visible focus ring on keyboard focus.
- No JS: the badge disappears (
no-js:hidden) but the slider stays fully operable — value, min, max, and the coloured fill are all rendered server-side.
Tokens
| Token | Used for |
|---|---|
--background-{hue} |
Filled portion of the track, thumb fill (per color) |
--background-neutral |
Unfilled portion of the track |
--background-page |
Thumb border |
--border-neutral |
Thumb outer shadow ring |
--outline-color-focus |
Focus ring on the thumb |
--background-inverted / --on-inverted |
Badge |
Do / Don’t
- Do pair the slider with a visible value or unit nearby when precision matters — the badge is a nice-to-have echo, not a substitute for feedback.
- Do set sensible
min/max/stepfor the value being edited (percentages: 0–100 step 1; ratings: 0–5 step 0.5). - Don’t rely on the badge alone to communicate the value to screen reader users — it’s decorative.
- Don’t omit
aria-label(or an associated<label>) — without one the slider has no accessible name.
Content
No visible label text is rendered by the component — write aria-label (or an
external label) as a short noun phrase (“Volume”, “Price range”), not an
instruction (“Drag to adjust volume”).