Slider

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless slider
Source View Source

Overview of the Phoenix and LiveView Headless Slider Component

The Mishka Chelekom Headless Slider ships zero opinionated styling - only the semantic markup, full WAI-ARIA wiring, pointer drag, and keyboard support (Arrow keys, Shift+Arrow / PageUp/PageDown, Home/End). Pass a single value or a list of values for a range with two or more thumbs; range thumbs respect min_steps_between_values and a configurable thumb_collision behavior. Every part - label, value readout, control, track, indicator, and thumb - is addressable through a *_class attribute, so you style it entirely with your own Tailwind classes while the accessibility and behavior keep working underneath.

The root takes class. The parts take label_class, value_class, control_class, track_class, indicator_class and thumb_class (every thumb). The label is drawn when label is set, and the value readout when show_value is.

Base UI Slider example (Phoenix headless)

Base UI Slider example (Phoenix headless)

heex
<.slider
  id="slider-hero"
  value={25}
  label="Volume"
  control_class="flex w-56 touch-none items-center py-3 select-none"
  track_class="h-1 w-full bg-neutral-200 select-none dark:bg-neutral-800"
  indicator_class="bg-neutral-950 select-none dark:bg-white"
  thumb_class="size-4 border border-neutral-950 bg-white select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white dark:border-white dark:bg-neutral-950"
/>

Range Slider

values takes a list, and each number is a thumb. thumb_labels gives every thumb its own accessible name. The filled indicator runs from the first thumb to the last.

Range Slider

heex
<.slider
  id="slider-range-slider"
  values={[25, 45]}
  thumb_labels={["Minimum value", "Maximum value"]}
  control_class="flex w-56 touch-none items-center py-3 select-none"
  track_class="h-1 w-full bg-neutral-200 select-none dark:bg-neutral-800"
  indicator_class="bg-neutral-950 select-none dark:bg-white"
  thumb_class="size-4 border border-neutral-950 bg-white select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white dark:border-white dark:bg-neutral-950"
/>

Edge Alignment

Edge Alignment

heex
<.slider
  id="slider-edge-alignment"
  value={25}
  label="Volume"
  control_class="flex w-56 touch-none items-center py-3 select-none"
  track_class="h-1 w-full bg-neutral-200 select-none dark:bg-neutral-800"
  indicator_class="bg-neutral-950 select-none dark:bg-white"
  thumb_class="size-4 border border-neutral-950 bg-white select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white dark:border-white dark:bg-neutral-950"
/>

Vertical

Vertical

heex
<.slider
  id="slider-vertical"
  value={35}
  orientation="vertical"
  label="Volume"
  control_class="flex touch-none select-none data-[orientation=vertical]:h-32 data-[orientation=vertical]:px-3"
  track_class="bg-neutral-200 select-none dark:bg-neutral-800 data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1"
  indicator_class="bg-neutral-950 select-none dark:bg-white"
  thumb_class="size-4 border border-neutral-950 bg-white select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white dark:border-white dark:bg-neutral-950"
/>

Slider example - label and value readout (Phoenix headless)

show_value draws the readout and label the label, both beside the control, so the root here is a two-column grid with the control spanning both. format takes Intl.NumberFormat options and locale a BCP-47 tag, and the hook keeps the readout current while a thumb moves.

Slider example - label and value readout (Phoenix headless)

20 – 60
heex
<.slider
  id="slider-readout"
  values={[20, 60]}
  format={%{style: "currency", currency: "USD", maximumFractionDigits: 0}}
  locale="en-US"
  label="Price range"
  show_value
  class="grid w-56 max-w-full grid-cols-2 gap-y-2"
  label_class="text-sm font-normal text-neutral-950 dark:text-white"
  value_class="text-right text-sm tabular-nums text-neutral-950 dark:text-white"
  control_class="col-span-2 flex touch-none items-center py-3 select-none"
  track_class="h-1 w-full bg-neutral-200 select-none dark:bg-neutral-800"
  indicator_class="bg-neutral-950 select-none dark:bg-white"
  thumb_class="size-4 border border-neutral-950 bg-white select-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white dark:border-white dark:bg-neutral-950"
/>

Parts & data attributes

Each part carries a stable data-part and a structural chelekom-slider__* class you can target, plus orientation and drag state for styling.

Partdata-partState / ARIAPurpose
Root-data-orientation, data-dragging, data-disabledCarries the Slider hook; reads min/max/step/large_step/orientation/format from data-*; takes class.
Labellabel-Accessible label, wired via aria-labelledby; drawn when label is set, and takes label_class.
Valuevaluedata-orientationFormatted value readout, kept current as the thumbs move; drawn when show_value is set, and takes value_class.
Controlcontroldata-orientation, data-dragging, data-disabledPress to jump the nearest thumb; drag to move it; takes control_class.
Tracktrackdata-orientationThe visual track the thumbs travel along; takes track_class.
Indicatorindicatordata-orientationThe filled interval from the first thumb (or min) to the last; takes indicator_class.
Thumbthumbrole="slider", aria-orientation, aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext, data-index, data-orientation, data-dragging, data-disabledOne per value; contains a hidden input for form submission; takes thumb_class.
Inputinput-Hidden value carrier (single → name, range → name[]).

Accessibility

Follows the WAI-ARIA Slider (and multi-thumb) pattern: each thumb is a role="slider" element focusable with Tab. Arrow keys move by step (Up/Right increase), Shift+Arrow or PageUp/PageDown move by large_step, and Home/End jump to the minimum/maximum. Range thumbs keep their configured minimum gap and never cross past each other unexpectedly, per the chosen thumb_collision behavior.