Slider

ui-slider wraps a native <input type="range"> and fills the track up to the thumb. Its value is a number, kept in sync while the thumb moves. It is form-associated, so the value is submitted under its name, and it keeps the native range input's keyboard support.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <div class="demo-narrow">
    <ui-slider aria-label="Volume" value="50"></ui-slider>
  </div>
</body>
</html>

Examples

Range and step

min, max and step work as on the native input.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-stack {
      display: grid;
      gap: calc(var(--spacing) * 4);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <div class="demo-narrow demo-stack">
    <ui-slider aria-label="Opacity" value="0.6" min="0" max="1" step="0.1"></ui-slider>
    <ui-slider aria-label="Temperature" value="21" min="16" max="28" step="0.5"></ui-slider>
  </div>
</body>
</html>

Showing the value

The slider does not print its value. Listen for input to show it while dragging.

Previewindex.htmlslider-value-demo.html
Brightness %
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/label.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <import-element from="./slider-value-demo.html"></import-element>
  <slider-value-demo></slider-value-demo>
</body>
</html>
<custom-element name="slider-value-demo">
  <script>
    const brightness = useState({ value: 70 });

    function moved(slider) {
      brightness.value = slider.value;
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 3);
      width: 100%;
      max-width: calc(var(--spacing) * 72);
    }
    .row {
      display: flex;
      justify-content: space-between;
      font-size: var(--text-sm);
    }
    .value {
      color: var(--muted-foreground);
      font-variant-numeric: tabular-nums;
    }
  </style>

  <div class="row">
    <ui-label for="slider-brightness">Brightness</ui-label>
    <span class="value"><slot name="#brightness.value"></slot>%</span>
  </div>
  <ui-slider id="slider-brightness" value="70" oninput="moved(this)"></ui-slider>
</custom-element>

Custom fill colour

The filled part of the track uses --primary. Override the token on an ancestor, for example with a class, to change it.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <style>
    .slider-risk {
      --primary: var(--destructive);
    }
  </style>
  <div class="demo-narrow slider-risk">
    <ui-slider aria-label="Risk" value="80"></ui-slider>
  </div>
</body>
</html>

Disabled

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <div class="demo-narrow">
    <ui-slider aria-label="Locked" value="30" disabled></ui-slider>
  </div>
</body>
</html>

In a form

The value is submitted as a number string under name. Reset returns to the value attribute.

Previewindex.htmlslider-form-demo.html
Apply Reset
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Slider demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <import-element from="./slider-form-demo.html"></import-element>
  <slider-form-demo></slider-form-demo>
</body>
</html>
<custom-element name="slider-form-demo">
  <script>
    const result = useState({ text: "Submit the form to see its data." });

    function submit(event) {
      event.preventDefault();
      result.text = [...new FormData(event.target)].map(([name, value]) => `${name}=${value}`).join("\n");
    }

    function reset() {
      result.text = "The form was reset.";
    }
  </script>

  <style>
    :host {
      display: grid;
      gap: calc(var(--spacing) * 4);
      width: 100%;
      max-width: calc(var(--spacing) * 80);
    }
    form {
      display: grid;
      gap: calc(var(--spacing) * 4);
    }
    .actions {
      display: flex;
      gap: calc(var(--spacing) * 2);
    }
    output {
      display: block;
      padding: calc(var(--spacing) * 3);
      border-radius: var(--radius-md);
      background: var(--muted);
      font-family: var(--font-mono);
      font-size: var(--text-xs);
      white-space: pre-wrap;
    }
  </style>

  <form onsubmit="submit(event)" onreset="reset()">
    <ui-slider name="budget" aria-label="Budget" value="400" min="0" max="1000" step="50"></ui-slider>
    <ui-slider name="quality" aria-label="Quality" value="3" min="1" max="5"></ui-slider>
    <div class="actions">
      <ui-button type="submit">Apply</ui-button>
      <ui-button type="reset" variant="outline">Reset</ui-button>
    </div>
  </form>
  <output><slot name="#result.text"></slot></output>
</custom-element>

API

Attributes

Attribute Type Default Description
value number 0 Current value. The attribute is the initial and reset value.
min number 0 Lowest value.
max number 100 Highest value.
step number 1 Granularity of the value.
disabled boolean false Disables the slider. Also true inside a disabled <fieldset>.
aria-label string Accessible name, passed to the inner input. Property name: label.

Properties

Property Type Description
value number Current value as a number. Updated while the thumb moves. Setting it moves the thumb.

Events

Event Detail Description
input none The inner range input's native input event (composed), fired while dragging.
change none Dispatched from the element (bubbling) when the user releases the thumb or commits a key press.

CSS parts

Part Description
control The inner native <input type="range">.

CSS custom properties

Property Description
--state-range-percent Set on the host to the filled share of the track, for example 40%. Read-only. Use it to draw a matching tooltip or label.

Forms

ui-slider is form-associated and submits value under its name. form.reset() restores the value attribute, and a disabled <fieldset> disables it.

Accessibility

The host delegates focus to the inner native range input, which has the slider role and native keyboard support. The arrow keys change the value by step, Page Up and Page Down by larger amounts, and Home and End jump to min and max.

Edit this page on GitHub · View as Markdown