Usage
Use [data-slider] around one or two native input[type="range"].form-range controls. Bootstrap keeps the real form control semantics, keyboard behavior, disabled state, and Sass customization path; Moo UI adds the shadcn-like filled track, value output synchronization, and public lifecycle wrapper.
The optional Slider ESM is small and self-contained. Import @wpmoo/ui/slider.js, initialize only the root your page owns, and call dispose() before removing that root in a partial-page or SPA update. Importing the module never scans the document.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 50.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<label class="form-label mb-0" for="slider-default">Volume</label>
<output class="small text-body-secondary" for="slider-default" data-slider-output>50</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-default"
type="range"
min="0"
max="100"
step="1"
value="50"
data-slider-input
name="volume" >
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42<div class="field w-100">
<div
class="slider slider--range"
data-slider
data-slider-orientation="horizontal"
role="group"
aria-labelledby="slider-range-label"
style="--moo-slider-start: 25.0%; --moo-slider-end: 75.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<span class="form-label mb-0" id="slider-range-label">Price range</span>
<output class="small text-body-secondary" for="slider-range-start slider-range-end" data-slider-output>25 - 75</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-range-start"
type="range"
min="0"
max="100"
step="5"
value="25"
data-slider-input
data-slider-thumb="start"
aria-label="Minimum Price range"
name="price_min" >
<input
class="form-range slider-input slider-input--upper"
id="slider-range-end"
type="range"
min="0"
max="100"
step="5"
value="75"
data-slider-input
data-slider-thumb="end"
aria-label="Maximum Price range"
name="price_max" >
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 70.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<label class="form-label mb-0" for="slider-steps">Rating</label>
<output class="small text-body-secondary" for="slider-steps" data-slider-output>3.5</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-steps"
type="range"
min="0"
max="5"
step="0.5"
value="3.5"
data-slider-input
name="rating" >
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<div class="field w-100">
<div
class="slider slider--vertical"
data-slider
data-slider-orientation="vertical"
style="--moo-slider-start: 0%; --moo-slider-end: 70.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<label class="form-label mb-0" for="slider-vertical">Gain</label>
<output class="small text-body-secondary" for="slider-vertical" data-slider-output>70</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-vertical"
type="range"
min="0"
max="100"
step="1"
value="70"
data-slider-input
aria-orientation="vertical"name="gain" >
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 40.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<label class="form-label mb-0" for="slider-disabled">Disabled volume</label>
<output class="small text-body-secondary" for="slider-disabled" data-slider-output>40</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-disabled"
type="range"
min="0"
max="100"
step="1"
value="40"
data-slider-input
name="disabled_volume"disabled >
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<div dir="rtl" class="w-100">
<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 60.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<output class="small text-body-secondary" for="slider-rtl-arabic" data-slider-output>60</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-rtl-arabic"
type="range"
min="0"
max="100"
step="1"
value="60"
data-slider-input
name="rtl_arabic_volume"aria-label="مستوى الصوت" >
</div>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<div dir="rtl" class="w-100">
<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 60.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<output class="small text-body-secondary" for="slider-rtl-hebrew" data-slider-output>60</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-rtl-hebrew"
type="range"
min="0"
max="100"
step="1"
value="60"
data-slider-input
name="rtl_hebrew_volume"aria-label="עוצמת קול" >
</div>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<div dir="ltr" class="w-100">
<div class="field w-100">
<div
class="slider"
data-slider
data-slider-orientation="horizontal"
style="--moo-slider-start: 0%; --moo-slider-end: 60.0%;"
>
<div class="d-flex align-items-center justify-content-between gap-3">
<output class="small text-body-secondary" for="slider-rtl-english" data-slider-output>60</output>
</div>
<div class="slider-track" data-slider-track>
<div class="slider-range" data-slider-range></div>
<input
class="form-range slider-input"
id="slider-rtl-english"
type="range"
min="0"
max="100"
step="1"
value="60"
data-slider-input
name="rtl_english_volume"aria-label="Volume" >
</div>
</div>
</div>
</div>
JavaScript
The module is explicit and idempotent. It does not replace the native range
control with a custom ARIA widget; it only synchronizes fill and output state
around real inputs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14import Slider from "@wpmoo/ui/slider.js";
const element = document.querySelector("[data-slider]");
if (element) {
const slider = Slider.getOrCreateInstance(element);
const again = Slider.getOrCreateInstance(element);
console.assert(slider === again);
slider.dispose();
}
Component reference
- Status
- Accepted
- Runtime
- optional Moo UI ESM
- Markup
- Moo UI documented extension
- Reference
-
For Bootstrap's native range input semantics, disabled state, min/max, step, and Sass variables, see the
Bootstrap Range documentation.