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

live
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.

PropTypeDefaultDescription
bindnumberTwo-way bound value. .set() fires onchange; plain assignment is silent. two-way CURRENT value (unbound: starts at min)
minnumber0lower bound
maxnumber100upper bound
stepnumber1snapping increment (0/invalid → 1)
marksbooleanfalsetick marks at every step (when feasible)
labelstring''text label above the track
disabledbooleanfalseblocks interaction
showvaluebooleanfalsevalue bubble above the thumb while dragging/focused
colorstring''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.