Slider
FreeRange slider with single-value and dual-handle range mode, tick marks and step control.
</>sv-slider sliderrangeinput
import { SvSliderComponent } from '@styloviz/forms';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Dual-handle range
Enable range to select a min/max band.
example.htmlAngular
1 lines
<sv-slider [range]="true" [min]="0" [max]="1000" [(rangeStart)]="lo" [(rangeEnd)]="hi" suffix="$" />
Ticks & value label
example.htmlAngular
1 lines
<sv-slider [min]="0" [max]="10" [step]="1" [showTicks]="true" [showValue]="true" [(ngModel)]="rating" />
When to use
- Choosing an approximate numeric value within a known range (volume, price, opacity).
- You need a price/value band — use `range` for dual handles.
- When the exact number matters, pair with or prefer a Number Input.
Async states
None — this component has nothing to load, so it has no loading, empty or error state to drive.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| value | number (two-way) | 50 | Single slider value. Use `[(value)]` for two-way binding. |
| rangeStart | number (two-way) | 20 | Range slider lower bound. Use with `range=true`. |
| rangeEnd | number (two-way) | 80 | Range slider upper bound. Use with `range=true`. |
| range | boolean | false | Enable range (two-handle) mode. |
| minGap | number | null | null | Smallest allowed distance between the two handles. `null` uses one `step`. |
| showInputs | boolean | false | Render a number field bound to each handle. |
| min | number | 0 | Minimum value. |
| max | number | 100 | Maximum value. |
| step | number | 1 | Step increment. |
| label | string | '' | Optional label above the slider. |
| inputId | string | '' | Explicit id forwarded to the (single) native input. Auto-generated when omitted. |
| ariaLabel | string | '' | `aria-label` for the slider when there is no visible `label`. |
| showValue | boolean | true | Show the current value. THIS USED TO BE IGNORED IN RANGE MODE. The single slider honoured it; the two-handle one drew its value bubbles unconditionally, so a host that had already rendered a formatted range above the track — the normal thing to do for money, where `4000000` is not a label anybody wants — got the raw numbers printed on top of it anyway, with no way to turn them off. One input, one meaning, both modes. |
| valueFormat | ((value: number) => string) | null | null | Turn a raw value into the text on the bubbles and the min/max labels. A price range slider is the case this exists for. Without it the scale under the track reads `200000 … 4000000`, which is a number a computer wrote; with `formatMoneyShort` it reads `$200k … $4M`. `prefix`/`suffix` cannot do this — they concatenate, they do not format, so no combination of them produces a grouped or abbreviated figure. Pass a STABLE reference (a class field or a bound method), not an arrow written in the template: a new function identity on every change detection pass defeats the memoisation on the computed signals below. |
| prefix | string | '' | Text prefix for value display (e.g. '$'). |
| suffix | string | '' | Text suffix for value display (e.g. '%'). |
| showTicks | boolean | false | Show tick marks below the track (single mode only). |
| tickCount | number | 5 | Number of visible tick marks (evenly spaced). |
| disabled | boolean | false | Disable interaction. |
| variant | SliderVariant | 'primary' | Color variant. |
| size | SliderSize | 'md' | Size — controls track height and thumb size. |
| errorMessage | string | '' | Client-side validation message. |
| serverError | SvServerError | null | A rejection reported by the server about this value. Self-expiring. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| rangeChange | [number, number] | Emits updated range as [start, end]. |
| valueChange | number | Emits the new `value` whenever it changes — the output half of `[(value)]`. |
| rangeStartChange | number | Emits the new `rangeStart` whenever it changes — the output half of `[(rangeStart)]`. |
| rangeEndChange | number | Emits the new `rangeEnd` whenever it changes — the output half of `[(rangeEnd)]`. |
Customization & theming
- Single value (`[(value)]`) or dual-handle range (`[(rangeStart)]` / `[(rangeEnd)]`, `range="true"`).
- Configure `min`, `max`, `step`, optional `showTicks` / `tickCount`, and a `prefix` / `suffix` value readout (`showValue`).
- Pick a `variant` and `size`; name the control with `label` or `ariaLabel`.
Best practices
- Do: Show the current value (`showValue`) or a bound readout so the choice is legible.
- Do: Choose a `step` that matches the precision users actually need.
- Don't: Don't use a slider for precise or wide-range entry (e.g. 1–1,000,000) — type it instead.
Accessibility
- Built on the native `<input type="range">`, so it has implicit `role="slider"` and full keyboard support — arrows nudge by `step`, Home/End jump to bounds, PageUp/PageDown step larger.
- The single slider associates its `label` via `for`/`id` (or set `ariaLabel`); `prefix`/`suffix` are announced through `aria-valuetext` (e.g. "70%").
- Range handles expose `aria-valuetext` and dynamic `aria-valuemin`/`aria-valuemax` (each handle bounded by the other); the thumb shows a clear focus ring.
- Implements ControlValueAccessor for the single value — works with `[(ngModel)]` and reactive forms; `formControl.disable()` disables the slider.
- Right-to-left is handled by the platform for the keyboard and by CSS for the layout: the native input flips Left/Right and the thumb itself, and the fill, bubbles and insets use `inset-inline-*` so they resolve from the same `direction` rather than a second guess at it. The component adds no key handler of its own — that would have to re-derive the platform's behaviour and would disagree with it somewhere.
- `aria-valuenow`, `aria-valuemin` and `aria-valuemax` are numbers and do not flip: a screen reader announces 40 of 100 whichever way the track is drawn.
Feedback on Slider
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.