JavaScript Slider
@lemonadejs/slider · ✓ 20 contract checks · framework-agnostic · zero dependencies
(new in v6: no v5 source)
A horizontal slider on the Studio contract model. The bound state is the
CURRENT number (bind); the geometry is fully deterministic from
value/min/max — the filled track is a width% inline style and the thumb
a left% inline style, so position is testable without layout. The
pointer-position → value mapping is the only layout-dependent piece
(getBoundingClientRect of the track, captured once per gesture).
Commit model (v5-style): oninput fires on every value CHANGE while interacting — drag move, track-click jump, keyboard step (no echo on repeats) onchange fires ONCE on release with the final value (and once per keyboard commit) — only when it differs from gesture start
External writes through the bound state are SILENT (no onchange, no oninput) and reposition the thumb — the Switch convention.
Keyboard (on the thumb, role=“slider”): Arrow ±step, Home → min, End → max, PageUp/PageDown ±10·step.
marks: Boolean — auto tick marks at every step when feasible (at most 100 marks; denser ranges render none). Labeled mark ARRAYS are not adopted: contract prop types are scalar by convention.
Other common slider props deliberately not adopted: orientation (horizontal only), range value arrays (bind is a single Number by contract), valueLabelDisplay/valueLabelFormat (showvalue Boolean instead), scale, track=“inverted”, size, slot/component customization, onChangeCommitted (folded into onchange).
Example
import { html } from 'lemonadejs';
import Slider from '@lemonadejs/slider';
const App = (props, { state }) => {
const budget = state(1500);
const guests = state(40);
return html`<div style="max-width:420px">
<${Slider} label="Budget (USD)" bind="${budget}" min="500" max="5000" step="100" showvalue />
<${Slider} label="Guests" bind="${guests}" min="10" max="120" step="10" marks color="green" />
<p>Planning for <b>${guests}</b> guests at about
<b>${() => '$' + Math.round(budget.value / guests.value)}</b> per head.</p>
</div>`;
};Installation
npm install @lemonadejs/slider
import Slider from '@lemonadejs/slider';
import '@lemonadejs/slider/style.css';
Three deployment forms, one component:
html`<${Slider} />` // by value (no registration)
setComponents({ Slider }); // then <Slider /> by name anywhere
createWebComponent(Slider); // <lm-slider> in plain HTML/any framework
Props
Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.
| Prop | Type | Default | Description |
|---|---|---|---|
bind | number | — | Two-way bound value. .set() fires onchange; plain assignment is silent. two-way CURRENT value (unbound: starts at min) |
min | number | 0 | lower bound |
max | number | 100 | upper bound |
step | number | 1 | snapping increment (0/invalid → 1) |
marks | boolean | false | tick marks at every step (when feasible) |
label | string | '' | text label above the track |
disabled | boolean | false | blocks interaction |
showvalue | boolean | false | value bubble above the thumb while dragging/focused |
color | string | '' | green |
Events
All event names are lowercase (the platform convention — LJS-305 warns otherwise).
onchange— fires on RELEASE with the final value (v5-style commit)oninput— fires on every value change while interacting
Styling
All classes follow the lm-slider-* convention; visual variants are data-*
attributes on the root. Override freely — there is no styling engine to fight.
Contract
The machine-readable schema ships with the package:
import contract from '@lemonadejs/slider/contract.json';
verify.json carries the conformance proof produced by verify(Slider).
Looking for the v5 plugin? See the archived v5 documentation.