Skip to content

Slider

<quiet-slider> stable since 1.0.0

Sliders let users select numeric values within a given range by moving a thumb along a track.

Less More
<quiet-slider
  label="Number of cats"
  description="Limit six per household"
  name="value" 
  value="3"
  min="0"
  max="6"
  with-markers
  with-tooltip
  with-references
>
  <span slot="reference">Less</span>
  <span slot="reference">More</span>
</quiet-slider>

Examples Jump to heading

Showing tooltips Jump to heading

Add the with-tooltip attribute to show a tooltip when the slider has focus or is dragged.

<quiet-slider 
  label="Quality"
  name="quality" 
  min="0"
  max="100"
  value="50"
  with-tooltip
></quiet-slider>

Setting min, max, and step Jump to heading

Use the min and max attributes to set a minimum and maximum value for the slider. Use the step attribute to change the granularity the value must adhere to.

<quiet-slider 
  label="Between zero and one"
  min="0"
  max="1"
  step="0.1"
  value="0.5"
  with-tooltip
></quiet-slider>

Showing markers Jump to heading

Add the --with-markers attribute to show visual markers at each step. Markers work best with sliders that have shorter ranges.

<quiet-slider 
  label="Size"
  name="size" 
  min="0"
  max="8"
  value="4"
  with-markers
></quiet-slider>

Adding references Jump to heading

Use the with-references attribute and the reference slot to add visual references below the slider. By default, references are positioned with space-between, making it easy to align them with starting, ending, and center markers.

Slow Medium Fast
<quiet-slider 
  label="Speed"
  name="speed" 
  min="1"
  max="5"
  value="3"
  with-markers
  with-references
>
  <span slot="reference">Slow</span>
  <span slot="reference">Medium</span>
  <span slot="reference">Fast</span>
</quiet-slider>

If you want to show a reference next to a specific marker, you can add position: absolute to it and set the left, right, top, or bottom property to a percentage that corresponds to the marker's position.

Formatting the value Jump to heading

You can format the value that gets shown in tooltips and/or announced by screen readers with the valueFormatter property. Using JavaScript, set the property to a function that accepts a numeric value and returns a string. The Intl.FormatNumber API can be really useful here.




<!-- Percent -->
<quiet-slider 
  id="slider__percent"
  label="Percentage"
  name="percentage"
  value="0.5"
  min="0"
  max="1"
  step=".01"
  with-tooltip
></quiet-slider><br>

<script>
  const percentSlider = document.getElementById('slider__percent');
  const formatter = new Intl.NumberFormat('en-US', { style: 'percent' });

  customElements.whenDefined('quiet-slider').then(() => {
    percentSlider.valueFormatter = value => formatter.format(value);
  });
</script>

<!-- Duration -->
<quiet-slider 
  id="slider__duration"
  label="Duration"
  name="duration"
  value="12"
  min="0"
  max="24"
  with-tooltip
></quiet-slider><br>

<script>
  const hourSlider = document.getElementById('slider__duration');
  const formatter = new Intl.NumberFormat('en-US', { style: 'unit', unit: 'hour', unitDisplay: 'long' });

  customElements.whenDefined('quiet-slider').then(() => {
    hourSlider.valueFormatter = value => formatter.format(value);
  });
</script>

<!-- Angle -->
<quiet-slider 
  id="slider__angle"
  label="Angle"
  name="angle"
  min="0"
  max="360"
  value="180"
  with-tooltip
></quiet-slider><br>

<script>
  const angleSlider = document.getElementById('slider__angle');
  const formatter = new Intl.NumberFormat('en-US', { style: 'unit', unit: 'degree', unitDisplay: 'narrow' });

  customElements.whenDefined('quiet-slider').then(() => {
    angleSlider.valueFormatter = value => formatter.format(value);
  });
</script>

<!-- Currency -->
<quiet-slider 
  id="slider__currency"
  label="Currency"
  name="currency"
  min="0"
  max="100"
  value="50"
  with-tooltip
></quiet-slider>

<script>
  const currencySlider = document.getElementById('slider__currency');
  const formatter = new Intl.NumberFormat('en-US', { 
    style: 'currency', 
    currency: 'USD', 
    currencyDisplay: 'symbol', 
    maximumFractionDigits: 0 
  });

  customElements.whenDefined('quiet-slider').then(() => {
    currencySlider.valueFormatter = value => formatter.format(value);
  });
</script>

Changing the orientation Jump to heading

To make a vertical slider, set the orientation attribute to vertical. Vertical sliders are centered and will span 100% of the available space by default.

<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
  <quiet-slider
    orientation="vertical" 
    label="Volume" 
    name="volume"
    value="65"
    style="width: 80px"
  ></quiet-slider>

  <quiet-slider
    orientation="vertical" 
    label="Bass" 
    name="bass"
    value="50"
    style="width: 80px"
  ></quiet-slider>

  <quiet-slider
    orientation="vertical" 
    label="Treble" 
    name="treble"
    value="40"
    style="width: 80px"
  ></quiet-slider>
</div>

Changing the size Jump to heading

Use the size attribute to change the slider's size.





<quiet-slider size="xs" value="50" label="Extra small"></quiet-slider><br>
<quiet-slider size="sm" value="50" label="Small"></quiet-slider><br>
<quiet-slider size="md" value="50" label="Medium"></quiet-slider><br>
<quiet-slider size="lg" value="50" label="Large"></quiet-slider><br>
<quiet-slider size="xl" value="50" label="Extra large"></quiet-slider>

Changing the indicator's offset Jump to heading

To make the selection more obvious, a filled indicator representing the current value is drawn from the minimum value of the slider. To change the offset from which the indicator is drawn, set the indicator-offset attribute to another value along the slider.

Lazy Zoomies
<quiet-slider
  label="Cat playfulness"
  description="Energy level during playtime"
  name="value" 
  value="0"
  min="-5"
  max="5"
  indicator-offset="0"
  with-markers
  with-tooltip
>
  <span slot="reference">Lazy</span>
  <span slot="reference">Zoomies</span>
</quiet-slider>

Disabling Jump to heading

Use the disabled attribute to disable the slider.

<quiet-slider label="Disabled" value="50" disabled></quiet-slider>

Using custom validation Jump to heading

Sliders don't have built-in validation attributes like many other form controls. However, you can use the custom-validity attribute to make the slider invalid and show a custom error message on submit. To clear the error, remove the attribute or set it to an empty string.


Submit
<form action="about:blank" method="get" target="_blank">
  <quiet-slider
    name="value"
    label="Select a value"
    description="This field will be invalid until the custom-validity attribute is removed"
    custom-validity="Not so fast, bubba!"
  ></quiet-slider>
  <br>
  <quiet-button type="submit" variant="primary">Submit</quiet-button>
</form>

Most validation attributes work exactly like their native counterparts. However, the custom-validity attribute is offered in lieu of the setCustomValidity() method. This allows you to declaratively set custom errors instead of having to call a method with JavaScript.

Styling validation Jump to heading

You can style valid and invalid sliders using the :valid and :invalid pseudo classes.


Submit Reset
<form action="about:blank" method="get" target="_blank" class="slider__validation-pseudo">
  <quiet-slider
    name="value"
    label="Select a value"
    custom-validity="Select a number greater than zero"
    min="0"
    max="5"
    value="0"
    with-markers
    with-tooltip
  ></quiet-slider>
  <br>
  <quiet-button type="submit" variant="primary">Submit</quiet-button>
  <quiet-button type="reset">Reset</quiet-button>
</form>

<style>
  .slider__validation-pseudo {
    quiet-slider:valid {
      outline: solid 2px var(--quiet-constructive-stroke-mid);
      outline-offset: 1rem;
    }

    quiet-slider:invalid {
      outline: solid 2px var(--quiet-destructive-stroke-mid);
      outline-offset: 1rem;
    }
  }
</style>

<script>
  const form = document.querySelector('.slider__validation-pseudo');
  const slider = form.querySelector('quiet-slider');

  async function updateValidity() {
    await slider.updateComplete;
    slider.customValidity = slider.value > 0 ? '' : 'Select a number greater than zero';
  }

  slider.addEventListener('quiet-input', () => updateValidity());
  form.addEventListener('reset', () => updateValidity());
</script>

However, these selectors will match even before the user has had a chance to fill out the form. More often than not, you'll want to use the user-valid and user-invalid custom states instead. This way, validation styles are only shown after the user interacts with the form control or when the form is submitted.


Submit Reset
<form action="about:blank" method="get" target="_blank" class="slider__validation-custom">
  <quiet-slider
    name="value"
    label="Select a value"
    custom-validity="Select a number greater than zero"
    min="0"
    max="5"
    value="0"
    with-markers
    with-tooltip
  ></quiet-slider>
  <br>
  <quiet-button type="submit" variant="primary">Submit</quiet-button>
  <quiet-button type="reset">Reset</quiet-button>
</form>

<style>
  .slider__validation-custom {
    quiet-slider:state(user-valid) {
      outline: solid 2px var(--quiet-constructive-stroke-mid);
      outline-offset: 1rem;
    }

    quiet-slider:state(user-invalid) {
      outline: solid 2px var(--quiet-destructive-stroke-mid);
      outline-offset: 1rem;
    }
  }
</style>

<script>
  const form = document.querySelector('.slider__validation-custom');
  const slider = form.querySelector('quiet-slider');

  async function updateValidity() {
    await slider.updateComplete;
    slider.customValidity = slider.value > 0 ? '' : 'Select a number greater than zero';
  }

  slider.addEventListener('quiet-input', () => updateValidity());
  form.addEventListener('reset', () => updateValidity());
</script>

Styling sliders Jump to heading

Sliders come with a simple, minimal appearance. Feel free to customize them with your own styles.

<div style="display: flex; gap: 2rem;">
  <div class="slider__touch">
    <quiet-slider 
      label="Brightness"
      orientation="vertical"
      name="value" 
      min="0"
      max="1"
      step=".01"
      value="0.6"
    >
    </quiet-slider>
    <quiet-icon name="sun"></quiet-icon>
  </div>

  <div class="slider__touch">
    <quiet-slider 
      label="Volume"
      orientation="vertical"
      name="value" 
      min="0"
      max="1"
      step=".01"
      value="0.4"
    ></quiet-slider>
    <quiet-icon name="volume"></quiet-icon>
  </div>
</div>

<style>
  .slider__touch {
    position: relative;
    max-width: fit-content;

    quiet-slider {
      --track-size: 4rem;
      max-width: fit-content;

      &::part(track) {
        background-color: var(--quiet-neutral-fill-softer);
        box-shadow: inset 0 1px 2px color-mix(in oklab, var(--quiet-neutral-fill-softer), black 5%);
        cursor: pointer;
        overflow: hidden;
      }

      &::part(indicator) {
        background-color: #22c55e;
        border-radius: 0;
      }

      &::part(thumb) {
        opacity: 0;
      }

      &::part(tooltip__arrow) {
        display: none;
      }

      &::part(tooltip__content) {
        background-color: var(--quiet-neutral-fill-louder);
        border: none;
        color: var(--quiet-primary-text-on-loud);
      }

      &::part(slider):focus-visible {
        outline: var(--quiet-focus-ring);
        outline-offset: var(--quiet-focus-offset);
      }
    }

    quiet-icon {
      position: absolute;
      bottom: 3.5rem;
      left: calc(50% - 1rem);
      font-size: 2rem;
      color: var(--quiet-strident);
      pointer-events: none;
    }
  }
</style>

<quiet-slider 
  id="slider__color"
  label="Color"
  name="color" 
  min="0"
  max="359"
  with-tooltip
  style="--hue: hsl(0deg 100% 50%);"
></quiet-slider>

<br>

<quiet-slider 
  id="slider__opacity"
  label="Opacity"
  name="opacity" 
  min="0"
  max="1"
  step=".01"
  with-tooltip
  style="--opacity: 0%;"
></quiet-slider>

<style>
  #slider__color,
  #slider__opacity {
    --track-size: 1.25em;
    --thumb-width: 1.5em;
    --thumb-height: 1.5em;

    &::part(track) {
      box-shadow: inset 0 0 0 0.0625em color-mix(in oklab, black, transparent 90%);
      border-radius: var(--quiet-border-radius);
    }

    &::part(thumb) {
      border: solid 0.0625em rgb(0 0 0 / 33%);
      border-radius: 50%;
      transition: 100ms scale ease;
    }

    &:state(dragging)::part(thumb),
    &::part(thumb):focus-visible {
      scale: 1.25;
    }

    &::part(thumb)::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      border-radius: inherit;
      border: solid 0.125em white;
      box-shadow: inset 0 0 0 0.0625em rgb(0 0 0 / 33%);
    }
  }

  #slider__color {
    &::part(track) {
      background-image: 
        linear-gradient(
          to right,
          rgb(255, 0, 0) 0%,
          rgb(255, 255, 0) 17%,
          rgb(0, 255, 0) 33%,
          rgb(0, 255, 255) 50%,
          rgb(0, 0, 255) 67%,
          rgb(255, 0, 255) 83%,
          rgb(255, 0, 0) 100%
        );
    }

    &::part(indicator) {
      display: none;
    }

    &::part(thumb) {
      background-color: var(--hue);
    }    
  }

  #slider__opacity {
    &::part(track) {
      background-color: transparent;
      background-size: 0.5em 0.5em;
      background-position:
        0 0,
        0 0,
        -0.25em -0.25em,
        0.25em 0.25em;
      background-image: linear-gradient(45deg, var(--quiet-neutral-fill-soft) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, var(--quiet-neutral-fill-soft) 75%),
        linear-gradient(45deg, transparent 75%, var(--quiet-neutral-fill-soft) 75%),
        linear-gradient(45deg, var(--quiet-neutral-fill-soft) 25%, transparent 25%);    }

    &::part(indicator) {
      width: 100% !important;
      background-color: transparent;
      background-image: linear-gradient(to right, #0000 0%, #000f 100%);
    }    

    &::part(thumb) {
      background-color: color-mix(in oklab, white, black var(--opacity));
    }
  }
</style>

<script>
  const colorSlider = document.getElementById('slider__color');
  const opacitySlider = document.getElementById('slider__opacity');
  const degreeFormatter = new Intl.NumberFormat('en-US', { style: 'unit', unit: 'degree', unitDisplay: 'narrow' });
  const percentFormatter = new Intl.NumberFormat('en-US', { style: 'percent' });

  customElements.whenDefined('quiet-slider').then(() => {
    // Set formatters for screen readers
    colorSlider.valueFormatter = value => degreeFormatter.format(value);
    opacitySlider.valueFormatter = value => percentFormatter.format(value);

    // Set the color slider's thumb color when the value changes
    colorSlider.addEventListener('input', () => {
      colorSlider.style.setProperty('--hue', `hsl(${colorSlider.value}deg 100% 50%)`);
    });

    // Set the opacity slider's thumb color when the value changes
    opacitySlider.addEventListener('input', () => {
      opacitySlider.style.setProperty('--opacity', `${opacitySlider.value * 100}%`);
    });
  });
</script>
Off Slow Fast
<quiet-slider
  id="slider__repeat"
  label="Key repeat rate"
  name="repeat" 
  value="5"
  min="0"
  max="7"
  with-markers
  with-references
>
  <span slot="reference">Off</span>
  <span slot="reference" class="repeat-slow">Slow</span>
  <span slot="reference">Fast</span>
</quiet-slider>

<style>
  #slider__repeat {
    --thumb-width: 0.75em;
    --thumb-height: 1.5em;
    --marker-height: 0.5em;

    &::part(track) {
      height: 1em;
      background: none;
    }

    &::part(track)::after {
      content: '';
      position: absolute;
      top: calc(50% - .125em);
      left: 0;
      width: 100%;
      height: .25em;
      background-color: var(--quiet-neutral-fill-soft);
    }

    &::part(indicator) {
      display: none;
    }

    &::part(thumb) {
      border-radius: 0.25em;      
      border: none;
      background-color: var(--quiet-neutral-fill-loud);
    }

    &::part(marker) {
      display: initial;
      border-radius: 0.125em;
      background-color: var(--quiet-neutral-fill-mid);
    }

    .repeat-slow {
      position: absolute;

      &:dir(ltr) {
        left: 14%;
      }

      &:dir(rtl) {
        right: 14%;
      }
    }
  }
</style>

API Jump to heading

Importing Jump to heading

The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.

CDN npm

To manually import <quiet-slider> from the CDN, use the following code.

import 'https://cdn.jsdelivr.net/npm/@quietui/quiet@1.0.0/dist/components/slider/slider.js';

To manually import <quiet-slider> from npm, use the following code.

import '@quietui/quiet/dist/components/slider/slider.js';

Slots Jump to heading

Slider supports the following slots. Learn more about using slots

Name Description
label The slider's label. For plain-text labels, you can use the label attribute instead.
description The slider's description. For plain-text descriptions, you can use the description attribute instead.
reference One or more reference labels to show visually below the slider.

Properties Jump to heading

Slider has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties

Property / Attribute Description Reflects Type Default
associatedForm A reference to the <form> associated with the form control, or null if no form is associated. HTMLFormElement
null
null
label The slider's label. If you need to provide HTML in the label, use the label slot instead. string
description The slider's description. If you need to provide HTML in the description, use the description slot instead. string
name The name of the slider. This will be submitted with the form as a name/value pair. string
value The slider's value. number 0
disabled Disables the slider. boolean false
readonly Makes the slider a read-only field. boolean false
orientation The orientation of the slider. 'horizontal'
'vertical'
'horizontal'
size The slider's size. 'xs'
'sm'
'md'
'lg'
'xl'
'md'
indicatorOffset
indicator-offset
The starting value from which to draw the slider's fill, which is based on its current value. number
form The form to associate this control with. If omitted, the closest containing <form> will be used. The value of this attribute must be an ID of a form in the same document or shadow root. string
min The minimum value allowed. number 0
max The maximum value allowed. number 100
step The granularity the value must adhere to when incrementing and decrementing. number 1
customValidity
custom-validity
You can provide a custom error message to force the slider to be invalid. To clear the error, set this to an empty string. string ''
autofocus Tells the browser to focus the slider when the page loads or a dialog is shown. boolean
tooltipDistance
tooltip-distance
The distance of the tooltip from the slider's thumb. number 8
tooltipPlacement
tooltip-placement
The placement of the tooltip in reference to the slider's thumb. 'top'
'right'
'bottom'
'left'
'top'
withMarkers
with-markers
Draws markers at each step along the slider. boolean false
withReferences
with-references
Renders the slider with the references slot. boolean false
withTooltip
with-tooltip
Draws a tooltip above the thumb when the control has focus or is dragged. boolean false
valueFormatter A custom formatting function to apply to the value. This will be shown in the tooltip and announced by screen readers. Must be set with JavaScript. Property only. (value: number) => string

Methods Jump to heading

Slider supports the following methods. You can obtain a reference to the element and call them like functions in JavaScript. Learn more about methods

Name Description Arguments
focus() Sets focus to the slider.
blur() Removes focus from the slider.
checkValidity() Checks if the form control has any restraints and whether it satisfies them. If invalid, false will be returned and the invalid event will be dispatched. If valid, true will be returned.
reportValidity() Checks if the form control has any restraints and whether it satisfies them. If invalid, false will be returned and the invalid event will be dispatched. In addition, the problem will be reported to the user. If valid, true will be returned.
stepDown() Decreases the slider's value by step. This is a programmatic change, so input and change events will not be emitted when this is called.
stepUp() Increases the slider's value by step. This is a programmatic change, so input and change events will not be emitted when this is called.

Events Jump to heading

Slider dispatches the following custom events. You can listen to them the same way was native events. Learn more about custom events

Name Description
change
input
quiet-blur Emitted when the slider loses focus. This event does not bubble.
quiet-change Emitted when the user commits changes to the slider's value.
quiet-focus Emitted when the slider receives focus. This event does not bubble.
quiet-input Emitted when the slider receives input.

CSS custom properties Jump to heading

Slider supports the following CSS custom properties. You can style them like any other CSS property. Learn more about CSS custom properties

Name Description Default
--track-size The height or width of the slider's track. 0.75em
--marker-width The width of each individual marker. 0.1875em
--marker-height The height of each individual marker. 0.1875em
--thumb-width The width of the thumb. 1.25em
--thumb-height The height of the thumb. 1.25em

CSS parts Jump to heading

Slider exposes internal elements that can be styled with CSS using the selectors shown below. Learn more about CSS parts

Name Description CSS selector
label The element that contains the sliders's label. ::part(label)
description The element that contains the slider's description. ::part(description)
slider The focusable element with role="slider". Contains the track and reference slot. ::part(slider)
track The slider's track. ::part(track)
indicator The colored indicator that shows from the start of the slider to the current value. ::part(indicator)
markers The container that holds all the markers when with-markers is used. ::part(markers)
marker The individual markers that are shown when with-markers is used. ::part(marker)
references The container that holds references that get slotted in. ::part(references)
thumb The slider's thumb. ::part(thumb)
tooltip The tooltip, a <quiet-tooltip> element. ::part(tooltip)
tooltip__tooltip The tooltip's tooltip part. ::part(tooltip__tooltip)
tooltip__content The tooltip's content part. ::part(tooltip__content)
tooltip__arrow The tooltip's arrow part. ::part(tooltip__arrow)

Custom States Jump to heading

Slider has the following custom states. You can target them with CSS using the selectors shown below. Learn more about custom states

Name Description CSS selector
disabled Applied when the slider is disabled. :state(disabled)
dragging Applied when the slider is being dragged. :state(dragging)
focused Applied when the slider has focus. :state(focused)
user-valid Applied when the slider is valid and the user has sufficiently interacted with it. :state(user-valid)
user-invalid Applied when the slider is invalid and the user has sufficiently interacted with it. :state(user-invalid)

Dependencies Jump to heading

Slider automatically imports the following elements. Sub-dependencies are also included in this list.

Search this website Toggle dark mode Get the code on GitHub Follow @quietui.org on Bluesky Follow @quiet_ui on X
    No results found