Slider

July 6, 2026 · View on GitHub

Numeric range input with a draggable thumb on a horizontal track. Single-value and two-thumb range modes.

Contract

<kp-slider> is a numeric input rendered as a horizontal track with one (or two, in range mode) draggable thumbs. Pointer drag, track click, and full keyboard control are all wired. The component implements ControlValueAccessor so ngModel / reactive forms work out of the box — value type is number in single mode and [number, number] in range mode.

Anatomy

Slider
├─ Value labels (optional)            — floating above each thumb
├─ Track wrapper
│   ├─ Track                          — empty background rail
│   ├─ Track fill                     — filled portion between thumbs / 0→value
│   ├─ Tick marks (optional)          — 5 evenly spaced dots on the track
│   └─ Thumb(s)                       — draggable handle (role=slider)
└─ Min / max labels (optional)        — below the track

Sizes

SizeTrackThumbTick
sm4px16px4px
md6px20px4px
lg8px24px4px

API

Inputs

NameTypeDefaultDescription
size'sm' | 'md' | 'lg''md'Size ramp
mode'single' | 'range''single'One thumb vs. two-thumb range
minnumber0Lower bound
maxnumber100Upper bound
stepnumber1Discrete step the value snaps to
showTicksbooleanfalseRender 5 tick marks across the track
showValueLabelbooleanfalseShow current value above each thumb
showLabelsbooleanfalseShow minLabel / maxLabel under the track
minLabelstring | nullnullOverride the numeric min label (e.g. '\$0')
maxLabelstring | nullnullOverride the numeric max label
valueFormatter(v: number) => string | nullnullCustom formatter for value labels
disabledbooleanfalseDisable interaction
ariaLabelstring''Accessible label for the single thumb
ariaLabelStartstring''Accessible label for the range start thumb
ariaLabelEndstring''Accessible label for the range end thumb
valuenumber | [number, number] | nullTwo-way bindable value

Outputs

NamePayloadFires when
valueChangenumber | [number, number]Thumb position commits a new value (drag, track click, keyboard)

Forms

Works with both template-driven and reactive forms via ControlValueAccessor. In mode="range", writeValue / ngModel is a [start, end] tuple.

<!-- Single -->
<kp-slider [(ngModel)]="volume"/>

<!-- Range -->
<kp-slider mode="range" [(ngModel)]="priceRange"/>

Variants

DimensionValues
Sizesm, md, lg
Modesingle, range
Staterest, hover, focus, disabled

States

StateBehavior
restTrack empty (grey) + filled portion (blue) + thumb
hoverThumb border darkens
focus4px halo ring around thumb
disabledGrey fill + thumb, no pointer interaction

Accessibility

  • Role: each thumb is role="slider"
  • ARIA: aria-valuemin, aria-valuemax, aria-valuenow, aria-orientation="horizontal" on every thumb
  • Keyboard (thumb-focused):
    • / / / — step by step
    • Shift + arrow — step by 10 × step
    • PageUp / PageDown — step by 10 × step
    • Home / End — jump to min / max
  • Pointer: click on bare track moves the nearest thumb; click + drag on a thumb moves that thumb. Pointer capture keeps drag active when the cursor leaves the track.
  • Range: start thumb can't pass end thumb and vice versa — values are clamped on commit.

Do / Don't

Do

  • Use showValueLabel for values the user needs to read precisely (price, opacity %).
  • Provide ariaLabel (or aria-labelledby on a FormField wrapper) so screen readers know what the slider controls.
  • Use step to snap to meaningful increments (e.g. 50 for price in dollars, 1 for count).

Don't

  • Don't rely on the slider for exact numeric entry — pair it with a read-out label or a separate number input when precision matters.
  • Don't use range mode for a single-value slider with an optional "minimum" — that's two unrelated values; use two sliders.

References

Changelog

  • 0.1.0 — Initial release. Single + range modes, 3 sizes, pointer + keyboard control, full CVA support.