Skip to component content

Select single values and ranges with native range input semantics, shadcn-like track/thumb styling, and optional value output.

60

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.

Default

A single native range input drives the filled track and output text.

Slider
50
<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>

Range

Two coordinated native range inputs preserve form submission while sharing one filled track.

Slider
Price range 25 - 75
<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>

Steps

Use the native min, max, and step attributes for snapping and form values.

Slider
3.5
<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>

Vertical

Set orientation="vertical" when the value control belongs beside a compact visual scale.

Slider
70
<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>

Disabled

The disabled state stays on the native input, so it cannot be focused, changed, or submitted.

Slider
40
<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>

RTL

The filled track uses logical inset properties, so it follows the surrounding text direction while the input keeps native range semantics.

60
<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>
60
<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>
60
<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>

HTML Anatomy

Selector Purpose
Root
[data-slider] Slider root and lifecycle target. It owns fill state and optional output synchronization.
Track
[data-slider-track] Visual track wrapper. It receives pointer clicks and positions the filled range.
[data-slider-range] Decorative fill element. It is not a progressbar and has no ARIA role.
input[type="range"].form-range[data-slider-input] Native range input. It owns keyboard behavior, form serialization, disabled state, and accessible slider semantics.
Output
[data-slider-output] Optional output element synchronized from the current native input value or value range.

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.

import Slider from "@wpmoo/ui/slider.js";

const element = document.querySelector("[data-slider]");

if (element) {
  const slider = Slider.getOrCreateInstance(element);

  // getOrCreateInstance is idempotent: the same instance is returned.
  const again = Slider.getOrCreateInstance(element);
  console.assert(slider === again);

  // Later, release event listeners before removing the root.
  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.

Settings

Theme
Choose how Moo UI looks on this device.
Sidebar
Choose how the sidebar sits on the page.
Theme Builder
Tune catalog tokens and chart colors for this browser.
Base color
Theme color
Chart color
Heading font
Body font
Radius
Direction
Choose the reading direction. RTL flips the layout live.
Reset

Restore the default appearance.