Slider
The <x-wirekit::slider> component is a styled wrapper around the native <input type="range">. It provides accent-colored track styling, size variants, and an optional live value display while preserving full browser accessibility.
Usage
Width
The slider fills its parent width. Constrain it via the parent element:
<div class="max-w-sm">
<x-wirekit::slider name="volume" :min="0" :max="100" :value="50" />
</div>
Size Variants
Step Marks
Pass marks to render tick marks along the track. A plain list ([0, 25, 50, 75, 100]) draws ticks at those positions; a value-to-label map ([0 => 'Min', 100 => 'Max']) adds labels underneath.
Marks that carry a meaning
A third shape gives a mark a description as well as a label — for the case where the positions mean something and the label alone cannot say what:
Hover a tick to read its meaning. This exists because the alternative is worse than it sounds: without it, a reader who wants to know what a position means has to move the slider to find out — changing the very thing they were still deciding about.
The description reaches a reader two ways, and both are needed. It becomes the tick's title, which is a pointer affordance — and there is no hover on touch, so title alone would hide the meaning from the readers most likely to be guessing at it. So it is also what the slider announces: as you move through the positions, a screen reader reads the description of the one you are on rather than the bare number. One position at a time, not every mark at once.
It also becomes what the slider announces. Where a mark has both, the description wins over the label: the label is what you see on the tick (−2), the description is what the position means (Single verdict), and the second is the useful one to hear. valueTextMap still overrides everything if you want to decouple the two entirely.
Both older shapes are untouched — a list and a label map render exactly as before, with no title and no description. The new shape is opt-in per mark, and a mark may carry a description with no label at all.
Value Tooltip
Set tooltip to float a value bubble above the thumb that follows it as the user drags — handy when there's no room for a persistent value display.
Custom Step
<x-wirekit::slider name="price" :min="0" :max="1000" :step="25" :value="250" :show-value="true" />
Livewire Integration
<x-wirekit::slider> keeps its displayed value in internal state seeded from the
value prop, so when binding with Livewire pass the bound property as :value
alongside wire:model to seed the initial display:
<x-wirekit::slider wire:model.live="volume" :value="$volume" min="0" max="100" />
Without :value the slider sits at min until the user interacts; wire:model
keeps it in sync afterward. This is the framework-agnostic seeding pattern
WireKit's stateful controls share — it works in plain Blade forms too.
Optimistic UI
Pass the name of the Livewire method the slider should call and the new value is shown straight away, while it goes:
<x-wirekit::slider
name="volume"
label="Volume"
:value="$volume"
optimistic="saveVolume"
/>
Load wirekit-optimistic.js alongside whichever bundle you already use — below it, in your layout:
@wirekitScripts
<script src="{{ asset('vendor/wirekit/wirekit-optimistic.js') }}"></script>
Try it
The demo below runs the real path: the change shows immediately, the outline says it is provisional, and the server's answer either confirms it silently or takes it back.
The <livewire:demos.…> wrapper above exists only on this site — it supplies the demo
methods so the page can show a real round trip. The block under it is what you write.
It sends when the gesture ends, not while it runs. Dragging produces a value per frame, and sending each one would be a hundred requests for a single decision. So the value goes once, when you let go — and immediately on an arrow key, because one keypress is already a finished decision. There is no settle delay anywhere in this: a timer would make the same gesture behave differently on a fast machine than on a slow one.
A refusal puts the thumb back where the gesture started — not where you released it. Both the announced value and the control itself return, so nothing is left showing a position the server declined.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string|null |
null |
Form field name |
error |
string|null |
null |
Validation message. Rendered below the control, announced politely, and wired with aria-invalid + aria-describedby |
hint |
string|null |
null |
Helper text below the control. An error replaces it — one message region, so the two cannot stack |
id |
string|null |
auto-generated | Element id |
min |
int |
0 |
Minimum value |
max |
int |
100 |
Maximum value |
step |
int|float |
1 |
Increment step |
value |
int|null |
min |
Initial value |
size |
string |
'md' |
'sm', 'md', 'lg' |
showValue |
bool |
false |
Show live value next to slider |
marks |
array |
[] |
Tick marks — a list of positions ([0, 50, 100]), a position-to-label map ([0 => 'Min', 100 => 'Max']), or a map to ['label' => …, 'description' => …] for positions whose meaning the label cannot carry. See Step Marks. |
valueTextMap |
array|null |
null |
A value => spoken-text map for aria-valuetext, decoupled from the visual ticks — show numeric ticks but announce semantic meaning ([1 => 'Low', 5 => 'High']). A caller aria-valuetext binding wins over both |
tooltip |
bool |
false |
Float a value bubble above the thumb that follows it |
disabled |
bool |
false |
Disabled state |
optimistic |
string|null |
null |
Livewire method to call when the gesture ends, showing the change before the server answers |
optimisticArgs |
array |
[] |
Extra arguments appended to the optimistic action call, after the new value — the row this control belongs to. |
scope |
string|null |
null |
Scoped personalization key |
Accessibility
- Wraps the native
<input type="range">— inherits all browser a11y features - Arrow keys move by
step, Home/End jump to min/max, PageUp/PageDown move by larger increments - Live value display uses
aria-live="polite"for non-intrusive AT announcements - Disabled state via native
disabledattribute
Keyboard Interaction
| Key | Action |
|---|---|
Tab |
Move focus to the slider thumb |
ArrowLeft / ArrowDown |
Decrease the value by step |
ArrowRight / ArrowUp |
Increase the value by step |
PageDown |
Decrease by a larger amount (typically 10× step) |
PageUp |
Increase by a larger amount |
Home |
Jump to min |
End |
Jump to max |
Pitfalls
- Don't use a slider for precise numeric input. Use
<x-wirekit::number-input>with a stepper. Sliders excel at fuzzy / relative values (volume, opacity) where exact numbers don't matter. - Don't omit a numeric label. Touch users can't see the thumb's exact position — pair the slider with a live-updating value display.
- A dragged slider with
wire:model.livefloods the server. The native range input fires a change on every step during a drag, so.livesends one Livewire round-trip per step. Usewire:model.live.debounce.300msto coalesce them, orwire:model.lazyto sync only when the drag ends.
Design Tokens
The slider relies primarily on the native <input type="range"> rendering for the track and thumb, which inherits the browser-default accent color. The wrapper around the value display uses these tokens:
| Token | Used for |
|---|---|
--text-wk-sm |
Live value font size |
--color-wk-text |
Live value text |
--padding-wk-x-sm |
Live value pill padding |
--opacity-wk-disabled |
Disabled visual weight |
Config Defaults
The defaults live in config/wirekit.php under components.slider. Override them globally:
'components' => [
'slider' => ['min' => 0, 'max' => 100, 'step' => 1, 'size' => 'md'],
],