Components Slider

Petal Pro is the full SaaS app this is built for

Auth, billing, admin, and Claude Code integration included. One purchase, unlimited projects.

Slider

Single or dual thumb, marks, vertical, and a value that shows inline or in a tooltip. The form posts plain numbers.
Single thumb

A native range input with the track, fill and thumb painted by the library, so it looks the same on every browser and OS. label names it for screen readers and prints above the track; show_value="inline" puts the live readout in that same row, with value_prefix and value_suffix formatting it.

heex
<div class="w-full max-w-sm space-y-8">
<.slider name="volume" label="Volume" value={60} value_suffix="%" show_value="inline" />
<.slider name="quality" label="Quality" value={3} min={1} max={5} />
</div>
Dual thumb

min_field and max_field make it a range: two thumbs, two form values, one fill spanning between them. Each thumb posts its own name, so a price filter is a plain form change event with no JavaScript of your own. Reversed or out-of-bounds values are clamped and ordered server-side, so the fill can never paint backwards.

heex
<div class="w-full max-w-sm">
<.slider
min_field={to_form(%{"min" => "250"}, as: :price)[:min]}
max_field={to_form(%{"max" => "750"}, as: :price)[:max]}
min={0}
max={1000}
step={50}
label="Price"
value_prefix="$"
show_value="inline"
/>
</div>
Marks and tooltip

marks are labelled stops under the track. An empty label renders a tick only, so you can mark every step and label the ones that carry meaning; ticks the fill has swallowed flip to the on-primary treatment. show_value="tooltip" floats the value in a bubble over the thumb while you drag or tab to it, rather than taking up a row.

heex
<div class="w-full max-w-sm">
<.slider
name="year"
label="Model year"
value={2010}
min={1990}
max={2030}
step={5}
show_value="tooltip"
marks={[
%{value: 1990, label: "1990"},
%{value: 2000, label: ""},
%{value: 2010, label: "2010"},
%{value: 2020, label: ""},
%{value: 2030, label: "2030"}
]}
/>
</div>
Sizes and states

size runs sm, md, lg - track thickness and thumb diameter move together, so the proportions hold. disabled dims the whole control and takes the native inputs out of the tab order.

heex
<div class="w-full max-w-sm space-y-6">
<.slider name="s" label="Small" value={30} size="sm" />
<.slider name="m" label="Medium" value={50} size="md" />
<.slider name="l" label="Large" value={70} size="lg" />
<.slider name="d" label="Disabled" value={40} disabled />
</div>
Vertical

orientation="vertical" stands the track up and fills from the bottom. It is the same native input turned with writing-mode, so the keyboard map and the screen reader announcement are unchanged - useful for a mixer strip or a level control that sits beside content rather than under it.

heex
<div class="flex items-end gap-10">
<.slider name="bass" label="Bass" value={70} orientation="vertical" show_value="tooltip" />
<.slider name="mid" label="Mid" value={45} orientation="vertical" show_value="tooltip" />
<.slider
name="treble"
label="Treble"
value={55}
orientation="vertical"
show_value="tooltip"
/>
</div>
Properties
Attribute Type Default Description
class any nil CSS class for the outer wrapper
disabled boolean false disables every input and dims the control
field any nil a Phoenix.HTML.FormField for single-thumb use; sets name, id and value
id string nil wrapper id; the inputs derive theirs from it
label string nil visible label above the track, and the base for each input's accessible name (dual thumbs become "<label> minimum" and "<label> maximum"); defaults to the humanised field name
marks list [] labelled stops rendered under the track, e.g. [%{value: 0, label: "Min"}, %{value: 50, label: ""}]; an empty label renders a tick only
max any 100 upper bound
max_field any nil FormField for the upper value; dual-thumb mode when set with min_field
max_name string nil upper input name in dual mode when not using max_field; defaults to "#{name}_max"
min any 0 lower bound
min_field any nil FormField for the lower value; dual-thumb mode when set with max_field
min_name string nil lower input name in dual mode when not using min_field; defaults to "#{name}_min"
name string nil input name when not using field
orientation string "horizontal" vertical stands the track up, filling from the bottom
one of: "horizontal", "vertical"
rest global any other HTML attributes, applied to the wrapper
show_value string "none" tooltip shows the value in a bubble above the thumb while dragging or focus-visible; inline renders it in the label row beside the track
one of: "tooltip", "inline", "none"
size string "md" track thickness and thumb diameter
one of: "sm", "md", "lg"
step any 1 step increment; values snap to it natively
value any nil current value (single-thumb)
value_prefix string "" prepended to displayed values, e.g. "$"
value_suffix string "" appended to displayed values, e.g. "%"
values list nil [min, max] current values (dual-thumb, when not using min_field/max_field)