Range
Select a value or an in/out interval with continuous, stepped, compact and expressive sliders.
Basic example
Choose a value with a mouse, touch, or keyboard. A rounded track shows the selected amount, while the circular thumb and focus ring follow your Material theme.
<div class="range-field">
<label for="volume">Volume</label>
<input id="volume" type="range" min="0" max="100" value="40"
data-value-label="false">
</div>All slider styling and behavior are included in kmaterialize. Existing input[type="range"] controls receive the new appearance. The native input preserves form submission and keyboard interaction.
Setup
Start with the kmaterialize CSS and JavaScript from Getting started, then follow the component setup below.
Ranges present when kmaterialize loads initialize automatically. For controls added later, call Range.init(input). Reinitialization safely replaces the previous instance.
const slider = Range.init(input, {
showValue: true, // Default: show a label during interaction.
showTicks: false
});
input.value = '75';
slider.update(); // Refresh after assigning a value in code.
// When removing the component:
slider.destroy();Examples and variants
Single slider with editable value
Add data-range-inputs to a single range input for one editable number on the left. The flat filled bar and tinted value segment follow the number-input appearance. Typing and dragging update each other. Decimal fields keep the precision of the step (for example, 3.0 with step 0.1), with centered, equal-width digits and a short vertical film-roll transition with subtle motion blur that clears as the value settles. Click the number to edit it directly; reduced-motion settings disable the animation. The floating value popup is hidden when the editable number is shown.
<div class="range-field">
<label for="amount">Amount</label>
<input id="amount" name="amount" type="range"
min="0" max="10" step="0.1" value="2.5" data-range-inputs>
</div>Alternatively, call Range.init(input, { showInput: true, inputLabel: 'Amount' }). The numeric field uses the slider’s bounds and step, supports form reset and disabled state, and normalizes empty or out-of-range drafts on blur. Only the original named range input submits a value. Set data-range-variant="outlined" or "rounded" on the input for another border treatment.
Steps and tick marks
Use step for discrete values and data-ticks for visible marks. Focus or drag the slider to reveal its value label.
<div class="range-field">
<label for="brightness">Brightness</label>
<input id="brightness" type="range" min="0" max="100"
step="10" value="60" data-ticks>
</div>Dense tick sets are thinned to at most 101 marks. step="any" allows continuous values and does not display ticks.
Formatted values
Use formatValue to give the value label and assistive technology a meaningful unit. The underlying form value remains numeric.
import { Range } from 'kmaterialize';
const input = document.querySelector<HTMLInputElement>('#range-budget')!;
Range.init(input, {
formatValue: value => `${value / 1000}k credits`
});Disabled
<div class="range-field">
<label for="unavailable">Unavailable setting</label>
<input id="unavailable" type="range" min="0" max="100"
value="30" disabled>
</div>In / out slider
Select a segment with two handles. The in point cannot pass the out point. Drag either handle or Tab to it and use the arrow keys, Home, or End. Both named inputs submit normally with a form.
<div class="range-interval" role="group" aria-label="Clip trim">
<input type="range" name="clipIn" aria-label="In point"
min="0" max="120" step="1" value="15">
<input type="range" name="clipOut" aria-label="Out point"
min="0" max="120" step="1" value="90">
</div>Place exactly two range inputs directly inside .range-interval, ordered in then out. Give each an accessible label. The first input defines the shared min, max, and step; matching attributes on both inputs also give sensible values before initialization. Set direction on the whole group.
Interval variants
Use the same component for price bounds, discrete selections, or continuous values. Add .range-compact or .range-expressive to a range field or interval group to change the track and handle sizes.
Price range · expressive · $50 increments
Continuous interval · compact
Right-to-left interval
Disabled interval
Set disabled on both inputs to disable the interval. Interval sliders support native step snapping; tick marks are available on the single-value sliders above. Drag the handles to adjust an interval; clicking the shared track does not move a handle.
<div class="range-interval range-expressive" role="group" aria-label="Price range">
<input type="range" aria-label="Minimum price" min="0" max="1000" step="50" value="200">
<input type="range" aria-label="Maximum price" min="0" max="1000" step="50" value="800">
</div>
<!-- Use step="any" on both inputs for continuous selection.
Use dir="rtl" on the group for right-to-left selection.
Use disabled on both inputs to disable both handles. -->Outside segments
With .range-outside, the track outside the in/out points is highlighted, the middle is muted, and end stops align with the handle centers at the minimum and maximum. This changes the appearance; values remain the ordered in/out points.
<div class="range-interval range-outside" role="group" aria-label="Outside segments">
<input type="range" aria-label="In point" min="0" max="100" value="25">
<input type="range" aria-label="Out point" min="0" max="100" value="75">
</div>Editable in / out fields
Add data-range-inputs to place an editable In value on the left and Out value on the right. Type a number, use the arrow keys, or drag a handle: both controls stay synchronized. No synchronization code is needed in your application. Floating value popups are hidden because both values are already displayed.
Filled · outside segments · 0.5 increments
Rounded · outside segments
<div class="range-interval range-outside" data-range-inputs
data-range-variant="filled" role="group" aria-label="In/out interval">
<input type="range" name="in" aria-label="In point"
min="-10" max="10" step="0.5" value="-5">
<input type="range" name="out" aria-label="Out point"
min="-10" max="10" step="0.5" value="5">
</div>data-range-variant supports filled (default), outlined, and rounded. You can also enable fields with RangeInterval.init(group, { showInputs: true, startLabel: 'In', endLabel: 'Out' }); change these labels for your language or use case.
Valid numbers update the slider immediately. Empty or incomplete drafts leave the handle unchanged. On change or blur, the field shows the bounded, step-aligned value; handles cannot cross. Dragging, API updates, disabling a range input, and resetting a form also update its number field. Only the named range inputs submit values, avoiding duplicate form entries. Number fields use raw numbers even when handle labels use formatValue.
Validation
Sliders always have a value, clamp to min/max, and do not support required or maxlength. Use an application-level message for rules involving other values or account limits.
The number above the slider is your selected storage allocation in GB. Drag the slider or use the arrow keys: the message clears at 50 GB or below and returns above that limit.
Application limit
error Your plan allows up to 50 GB. Reduce the allocation or change your plan.
<div class="range-field">
<div class="range-field-header">
<label for="validation-range">Storage allocation</label>
<output for="validation-range">80 GB</output>
</div>
<input id="validation-range" type="range" min="0" max="100" value="80" data-value-label="false" aria-invalid="true" aria-describedby="validation-range-error">
</div>
<p id="validation-range-error" class="supporting-text error-text" aria-live="polite"><i class="material-icons" aria-hidden="true">error</i> Your plan allows up to 50 GB. Reduce the allocation or change your plan.</p>import { Range } from 'kmaterialize';
const input = document.querySelector('#validation-range');
const output = document.querySelector('output[for="validation-range"]');
const error = document.querySelector('#validation-range-error');
const formatValue = value => `${value} GB`;
Range.init(input, { formatValue });
function update() {
const invalid = input.valueAsNumber > 50;
output.value = formatValue(input.valueAsNumber);
error.hidden = !invalid;
if (invalid) input.setAttribute('aria-invalid', 'true');
else input.removeAttribute('aria-invalid');
input.setCustomValidity(invalid ? 'Your plan allows up to 50 GB.' : '');
}
input.addEventListener('input', update);
input.addEventListener('change', update);
update();Reference
Updating a single slider
The library keeps the track and label synchronized on input, reset, resizing, and changes to min, max, step, or disabled. Arrow keys adjust the value; Home and End reach the limits. Label every slider with a linked label or aria-label.
The outputs in these demos use a standard input listener:
input.addEventListener('input', () => {
output.value = input.value;
});Interval API
Groups present when the library loads initialize automatically. Use RangeInterval.init(group, options) for dynamically inserted groups or custom formatting. Use .no-autoinit on a group to opt out of automatic interval initialization.
import { RangeInterval } from 'kmaterialize';
const group = document.querySelector<HTMLElement>('#range-price')!;
const interval = RangeInterval.init(group, {
showValue: true,
formatValue: value => `$${value}`
});
interval.setValues(250, 750);
const [start, end] = interval.getValues();
group.addEventListener('input', () => {
console.log(interval.getValues());
});
// Call interval.destroy() when removing the component.getValues() returns [in, out]. setValues(in, out) orders the values and applies native bounds and step rounding; it does not emit events. Non-finite values throw a TypeError. After assigning input values directly, call update(). Listen for bubbling native input and change events on the group; values are constrained before your listener runs. Reset restores the inputs’ original values.
Options: showInputs (default false), startLabel / endLabel (default In / Out), showValue (default true) and formatValue(value). Formatting is plain text and also supplies accessible value text. destroy() removes generated labels, listeners and interval styles, and restores managed attributes. Reinitialization safely replaces the old instance.