<arc-range-slider> Overview
Guidelines
When to use
- Provide a `label` so users understand what the range represents at a glance
- Use for selecting a sub-range within a larger set, such as a price filter, date range, or frequency band
- Use `arc-input` for real-time filtering or preview and `arc-change` for committing the selection to a server
- Choose `step` values that match your data granularity — use 1 for integers, 0.01 for fine decimal values
- Place the Range Slider in a container at least 250px wide for comfortable dual-thumb dragging
- Set `low` and `high` to sensible defaults that represent the most common range for your use case
When not to use
- Do not use Range Slider when the user only needs to pick a single value — use Slider instead
- Do not set a `step` so small that the two thumbs become difficult to separate with a mouse
- Do not omit `label` when the slider is standalone — without context the numeric readout is meaningless
- Do not use for non-numeric selections — use a multi-select or checkbox group instead
- Avoid placing multiple range sliders in a narrow column without sufficient vertical spacing
Features
- Custom dual-thumb track built with pointer capture for reliable cross-browser dragging
- Accent-primary filled region between the two thumbs visually indicates the selected range
- Header row displaying the label and "low – high" values in monospace font when `label` and `show-values` are set
- Configurable `min`, `max`, and `step` props for precise range and increment control
- Thumb hover and focus effects with scale-up and accent-primary glow shadow matching the single Slider
- Keyboard support: ArrowLeft/Right/Up/Down step by `step`, Home and End jump to limits
- Dual events: `arc-input` fires continuously during drag, `arc-change` fires on release, both with `{ low, high }` detail
- Full ARIA slider roles on each thumb with `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, and `aria-label`
- Clamping logic prevents thumbs from crossing each other, maintaining low ≤ high invariant
- Click-on-track moves the nearest thumb to the clicked position
- Disabled state at 50% opacity with pointer events blocked
- Reduced-motion media query disables thumb transitions
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<!-- Basic price range filter -->
<arc-range-slider label="Price" low="20" high="80" min="0" max="200" step="5"></arc-range-slider>
<!-- Temperature comfort zone -->
<arc-range-slider label="Comfort Zone" low="18" high="24" min="10" max="40" step="0.5"></arc-range-slider>
<!-- Frequency band selector -->
<arc-range-slider label="Frequency (Hz)" low="200" high="4000" min="20" max="20000" step="10"></arc-range-slider>
<!-- Disabled state -->
<arc-range-slider label="Locked Range" low="30" high="70" disabled></arc-range-slider>
<!-- Without value display -->
<arc-range-slider label="Silent" low="10" high="90" show-values="false"></arc-range-slider>
<script>
const rs = document.querySelector('arc-range-slider');
// Real-time filtering while dragging
rs.addEventListener('arc-input', (e) => {
console.log('Filtering:', e.detail.low, '–', e.detail.high);
});
// Commit final range on release
rs.addEventListener('arc-change', (e) => {
console.log('Committed:', e.detail.low, '–', e.detail.high);
});
</script> import { RangeSlider } from '@arclux/arc-ui-react';
import { useState } from 'react';
function PriceFilter() {
const [low, setLow] = useState(20);
const [high, setHigh] = useState(80);
return (
<div style={{ maxWidth: 400 }}>
<RangeSlider
label="Price"
low={low}
high={high}
min={0}
max={200}
step={5}
onArcInput={(e) => { setLow(e.detail.low); setHigh(e.detail.high); }}
onArcChange={(e) => fetchProducts(e.detail.low, e.detail.high)}
/>
</div>
);
}
function AudioEqualizer() {
const [band, setBand] = useState({ low: 200, high: 4000 });
return (
<div style={{ maxWidth: 400 }}>
<RangeSlider
label="Frequency Band (Hz)"
low={band.low}
high={band.high}
min={20}
max={20000}
step={10}
onArcInput={(e) => setBand({ low: e.detail.low, high: e.detail.high })}
/>
<p style={{ marginTop: 12, fontSize: 14, color: 'var(--text-muted)' }}>
Bandwidth: {band.high - band.low} Hz
</p>
</div>
);
} <script setup>
import { RangeSlider } from '@arclux/arc-ui-vue';
import { ref, computed } from 'vue';
const minAge = ref(18);
const maxAge = ref(65);
const range = computed(() => maxAge.value - minAge.value);
</script>
<template>
<div style="max-width:400px;">
<RangeSlider
label="Age Range"
:low="minAge"
:high="maxAge"
:min="0"
:max="100"
@arc-input="minAge = $event.detail.low; maxAge = $event.detail.high"
/>
<p style="margin-top:12px; font-size:14px; color:var(--text-muted);">
Span: {{ range }} years
</p>
</div>
</template> <script>
import { RangeSlider } from '@arclux/arc-ui-svelte';
let low = 25;
let high = 75;
function onInput(e) {
low = e.detail.low;
high = e.detail.high;
}
</script>
<div style="max-width:400px;">
<RangeSlider
label="Score Range"
{low}
{high}
min={0}
max={100}
on:arc-input={onInput}
on:arc-change={() => console.log('Committed:', low, '–', high)}
/>
<p style="margin-top:12px; font-size:14px; color:var(--text-muted);">
Selected: {low} – {high} ({high - low} points)
</p>
</div> import { Component } from '@angular/core';
import { RangeSlider } from '@arclux/arc-ui-angular';
@Component({
imports: [RangeSlider],
template: `
<div style="max-width:400px;">
<RangeSlider
label="Budget"
[low]="low"
[high]="high"
[min]="0"
[max]="10000"
[step]="100"
(arc-input)="onInput($event)"
(arc-change)="onCommit($event)"
></RangeSlider>
<p style="margin-top:12px; font-size:14px; color:var(--text-muted);">
${{ low | number }} – ${{ high | number }}
</p>
</div>
`,
})
export class BudgetFilterComponent {
low = 1000;
high = 5000;
onInput(e: CustomEvent) {
this.low = e.detail.low;
this.high = e.detail.high;
}
onCommit(e: CustomEvent) {
this.fetchResults(e.detail.low, e.detail.high);
}
fetchResults(low: number, high: number) { /* ... */ }
} import { RangeSlider } from '@arclux/arc-ui-solid';
import { createSignal } from 'solid-js';
function WeightFilter() {
const [low, setLow] = createSignal(50);
const [high, setHigh] = createSignal(150);
return (
<div style={{ 'max-width': '400px' }}>
<RangeSlider
label="Weight (kg)"
low={low()}
high={high()}
min={0}
max={300}
step={5}
onArcInput={(e) => { setLow(e.detail.low); setHigh(e.detail.high); }}
onArcChange={(e) => console.log('Final:', e.detail.low, '–', e.detail.high)}
/>
<p style={{ 'margin-top': '12px', 'font-size': '14px', color: 'var(--text-muted)' }}>
Range: {high() - low()} kg
</p>
</div>
);
} import { RangeSlider } from '@arclux/arc-ui-preact';
import { useState } from 'preact/hooks';
function DateRangeFilter() {
const [low, setLow] = useState(2010);
const [high, setHigh] = useState(2025);
return (
<div style={{ maxWidth: 400 }}>
<RangeSlider
label="Year Range"
low={low}
high={high}
min={1990}
max={2030}
step={1}
onArcInput={(e) => { setLow(e.detail.low); setHigh(e.detail.high); }}
onArcChange={(e) => fetchByYear(e.detail.low, e.detail.high)}
/>
<p style={{ marginTop: 12, fontSize: 14, color: 'var(--text-muted)' }}>
{high - low} years selected
</p>
</div>
);
} <!-- arc-range-slider is interactive — requires JS -->
<arc-range-slider></arc-range-slider> <!-- arc-range-slider is interactive — requires JS -->
<arc-range-slider></arc-range-slider> API
| Prop | Type | Default | Description |
|---|---|---|---|
min | number | 0 | Minimum allowed value at the left edge of the track. |
max | number | 100 | Maximum allowed value at the right edge of the track. |
step | number | 1 | Increment granularity. Values snap to multiples of this number. |
low | number | 0 | Lower bound value of the selected range. Reflected as an attribute. |
high | number | 100 | Upper bound value of the selected range. Reflected as an attribute. |
label | string | '' | Label text displayed above the slider with the range values shown on the right. |
show-values | boolean | true | Whether to display the numeric "low – high" readout in the header. |
disabled | boolean | false | Disables interaction, reducing opacity and blocking pointer events. |
Events
| Event | Description |
|---|---|
arc-input | Fired continuously as the user drags either thumb. Detail contains `{ low, high }`. Use for real-time filtering or preview. |
arc-change | Fired once when the user releases a thumb, indicating the final committed range. Detail contains `{ low, high }`. Use for persisting to a database or triggering an expensive operation. |
See Also
- Slider Range input slider with a label, live numeric value display, accent-primary fill track, and customisable min/max/step.
- Input Versatile form control supporting single-line text, email, password, and multiline textarea modes with built-in label, placeholder, and validation states. Pairs with Form for complete data-entry workflows.
- Number Input A numeric stepper input with decrement and increment buttons flanking a central text field, supporting min/max clamping, step increments, and keyboard shortcuts.