slider

The slider element can be used for numeric input. Optional labels can be displayed on the left or the right.

It also has an optional tooltip which is displayed on top of the slider thumb when the mouse is over it. The tooltip can either display the absolute or relative value and an optional unit.

As the slider uses the native input element, all attributes for this element are supported for the slider as well.

Each input can be referenced by multiple labels, but the input tag has a unique id that can be paired to only one label (through its same for attribute).
In other words, according to the Web Content Accessibility Guidelines (WCAG), it is highly recommended to use only one label together with the input.
When aria-valuenow holds a value that isn't comprehensible to the user, such as representing the day of the week with a numerical value, the user needs to set the aria-valuetext attribute as a string providing a clear description of the slider's value, like "Monday," to enhance user understanding.

component variations

Standard slider

<div class="a-slider">
  <input
    tabindex="0"
    id="slider1"
    type="range"
    min="0"
    max="100"
    step="1"
    aria-valuemin="0"
    aria-valuemax="100"
    aria-valuenow="50"
    aria-orientation="horizontal"
    aria-label="a standard slider"
    aria-description="here goes an extensive description text"
    value="50"
  />
</div>

Standard slider disabled

<div class="a-slider">
  <input
    tabindex="0"
    id="slider2"
    type="range"
    min="0"
    max="100"
    step="1"
    disabled=""
    aria-valuemin="0"
    aria-valuemax="100"
    aria-valuenow="50"
    aria-orientation="horizontal"
    aria-label="a standard slider disabled"
    aria-description="here goes an extensive description text"
    value="50"
  />
</div>

Standard slider with tooltip

50 %
<div class="a-slider">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider3"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="horizontal"
      aria-label="a standard slider with a tooltip"
      aria-description="here goes an extensive description text"
      value="50"
    />
  </div>
</div>

Standard slider with left label

50 %
<div class="a-slider">
  <label for="slider4">Label</label>
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider4"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-labelledby="a standard slider with left label"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="50"
    />
  </div>
</div>

Standard slider with right label

50 %
<div class="a-slider">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider5"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-labelledby="a standard slider with right label"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="50"
    />
  </div>
  <label for="slider5">Label</label>
</div>

Standard slider with left and right label

50 %
<div class="a-slider">
  <label for="slider6">Left</label>
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider6"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-labelledby="a standard slider with both labels"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="50"
    />
  </div>
  <label for="slider6">Right</label>
</div>

Standard slider with absolute value tooltip

501
<div class="a-slider a-slider--unitless">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      aria-haspopup="false"
    >
      501
    </span>
    <input
      tabindex="0"
      id="slider7"
      type="range"
      min="1"
      max="1000"
      step="1"
      aria-valuemin="1"
      aria-valuemax="1000"
      aria-valuenow="501"
      aria-orientation="horizontal"
      aria-label="a standard slider with a tooltip with absolute value"
      aria-description="here goes an extensive description text"
      value="501"
    />
  </div>
</div>

Standard slider with absolute value tooltip and unit

181°
<div class="a-slider">
  <label for="slider8">Degrees</label>
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      tooltip-unit="°"
      aria-haspopup="false"
    >
      181°
    </span>
    <input
      tabindex="0"
      id="slider8"
      type="range"
      min="1"
      max="360"
      step="1"
      aria-labelledby="a standard slider with a tooltip with absolute value and unit"
      aria-valuemin="1"
      aria-valuemax="360"
      aria-valuenow="181"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="181"
    />
  </div>
</div>

Standard slider with label left top

181°
<div class="a-slider a-slider--labels-on-top">
  <label for="slider9">Degrees</label>
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      tooltip-unit="°"
      aria-haspopup="false"
    >
      181°
    </span>
    <input
      tabindex="0"
      id="slider9"
      type="range"
      min="1"
      max="360"
      step="1"
      aria-labelledby="a standard slider with a label on the top left"
      aria-valuemin="1"
      aria-valuemax="360"
      aria-valuenow="181"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="181"
    />
  </div>
</div>

Standard slider with label right top

181°
<div class="a-slider a-slider--labels-on-top">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      tooltip-unit="°"
      aria-haspopup="false"
    >
      181°
    </span>
    <input
      tabindex="0"
      id="slider10"
      type="range"
      min="1"
      max="360"
      step="1"
      aria-labelledby="a standard slider with a label on the top right"
      aria-valuemin="1"
      aria-valuemax="360"
      aria-valuenow="181"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="181"
    />
  </div>
  <label for="slider10">Degrees</label>
</div>

Standard slider with label left and right top

181°
<div class="a-slider a-slider--labels-on-top">
  <label for="slider11">Degrees left</label>
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      tooltip-unit="°"
      aria-haspopup="false"
    >
      181°
    </span>
    <input
      tabindex="0"
      id="slider11"
      type="range"
      min="1"
      max="360"
      step="1"
      aria-labelledby="a standard slider with labels on top"
      aria-valuemin="1"
      aria-valuemax="360"
      aria-valuenow="181"
      aria-orientation="horizontal"
      aria-description="here goes an extensive description text"
      value="181"
    />
  </div>
  <label for="slider11">Degrees</label>
</div>

Standard slider with label left and right top, without tooltip

<div class="a-slider a-slider--labels-on-top">
  <label for="slider12">Degrees left</label>
  <input
    tabindex="0"
    id="slider12"
    type="range"
    min="1"
    max="360"
    step="1"
    aria-labelledby="a standard slider with labels on top without tooltip"
    aria-valuemin="1"
    aria-valuemax="360"
    aria-valuenow="181"
    aria-orientation="horizontal"
    aria-description="here goes an extensive description text"
    value="181"
  />
  <label for="slider12">Degrees</label>
</div>

slider vertical

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical">
  <input
    tabindex="0"
    id="slider13"
    type="range"
    min="0"
    max="100"
    step="1"
    aria-valuemin="0"
    aria-valuemax="100"
    aria-orientation="vertical"
    aria-label="a vertical slider"
    aria-description="here goes an extensive description text"
  />
</div>

slider vertical disabled

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical">
  <input
    tabindex="0"
    id="slider14"
    type="range"
    min="0"
    max="100"
    step="1"
    disabled=""
    aria-valuemin="0"
    aria-valuemax="100"
    aria-orientation="vertical"
    aria-label="a vertical slider disabled"
    aria-description="here goes an extensive description text"
  />
</div>

slider vertical with tooltip

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider15"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="vertical"
      aria-label="a vertical slider with tooltip"
      aria-description="here goes an extensive description text"
      value="50"
    />
  </div>
</div>

slider vertical with label

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="relative"
      aria-haspopup="false"
    >
      50 %
    </span>
    <input
      tabindex="0"
      id="slider16"
      type="range"
      min="0"
      max="100"
      step="1"
      aria-labelledby="a vertical slider with label"
      aria-valuemin="0"
      aria-valuemax="100"
      aria-valuenow="50"
      aria-orientation="vertical"
      aria-description="here goes an extensive description text"
      value="50"
    />
    <label for="slider16">Label</label>
  </div>
</div>

slider vertical with absolute value tooltip

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical a-slider--unitless">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      aria-haspopup="false"
    >
      501
    </span>
    <input
      tabindex="0"
      id="slider17"
      type="range"
      min="1"
      max="1000"
      step="1"
      aria-valuemin="1"
      aria-valuemax="1000"
      aria-valuenow="501"
      aria-orientation="vertical"
      aria-label="a vertical slider with tooltip with absolute value"
      aria-description="here goes an extensive description text"
      value="501"
    />
  </div>
</div>

slider vertical with absolute value tooltip and unit

This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<div class="a-slider a-slider--vertical">
  <div>
    <span
      class="a-tooltip -floating-shadow-s"
      tooltip-type="absolute"
      tooltip-unit="°"
      aria-haspopup="false"
    >
      181°
    </span>
    <input
      tabindex="0"
      id="slider18"
      type="range"
      min="1"
      max="360"
      step="1"
      aria-labelledby="a vertical slider with tooltip with absolute value and unit"
      aria-valuemin="1"
      aria-valuemax="360"
      aria-valuenow="181"
      aria-orientation="vertical"
      aria-description="here goes an extensive description text"
      value="181"
    />
    <label for="slider18">Degrees</label>
  </div>
</div>

additional content

styles SCSS

/* stylelint-disable no-descending-specificity */
/* stylelint-disable a11y/content-property-no-static-value */

@mixin thumb-base {
  border-radius: 50%;
  height: 1.5rem;
  margin-top: -0.71875rem;
  position: relative;
  width: 1.5rem;
  z-index: 999;
}

.a-slider {
  --slider-percentage: 50; // This initial value will be overwritten by JS.

  height: 3rem;
  width: auto;

  div {
    display: flex;
    position: relative;
    width: 100%;
    flex: 1 1 auto;
    margin: 0.5rem 0.75rem;
  }

  display: flex;
  align-items: center;

  input {
    appearance: none;
    background: transparent;
    height: 1.5rem;
    width: 100%;
    -webkit-appearance: none;
    outline: 0;
    position: relative;

    &::before,
    &::after {
      position: absolute;
      content: ' ';
      left: 0;
      right: 0;
      height: 0.125rem;
      top: 0.6875rem; // (thumbHeight - trackHeight) / 2 --> (1.5 - 0.125) / 2
    }

    // Even if the styling is the same, custom colors for Firefox need to be separated from Chrome.

    // Styling for the part not in progress of the slider.
    // Chrome
    &::before {
      background: var(--small__enabled__fill__default);
    }

    // Firefox
    &::-moz-range-track {
      background: var(--small__enabled__fill__default);
      height: 0.13rem;
    }

    // Styling for the part in progress of the slider.
    // Chrome
    &::after,
    &::-webkit-slider-thumb {
      background: var(--major-accent__enabled__fill__default);
      width: calc(var(--slider-percentage) * 1%);
    }

    // Firefox
    &::-moz-range-progress,
    &::-moz-range-thumb {
      background: var(--major-accent__enabled__fill__default);
    }

    &:hover {
      // Chrome
      &::after,
      &::-webkit-slider-thumb {
        background: var(--major-accent__enabled__fill__hovered);
      }

      // Firefox
      &::-moz-range-progress,
      &::-moz-range-thumb {
        background: var(--major-accent__enabled__fill__hovered);
      }
    }

    &:active {
      // Chrome
      &::after,
      &::-webkit-slider-thumb {
        background: var(--major-accent__enabled__fill__pressed);
      }

      // Firefox
      &::-moz-range-progress,
      &::-moz-range-thumb {
        background: var(--major-accent__enabled__fill__pressed);
      }
    }

    &:disabled {
      &::after,
      &::-webkit-slider-thumb {
        background: var(--major-accent__disabled__fill__default);
      }

      &::-moz-range-thumb,
      &::-moz-range-progress {
        background: var(--major-accent__disabled__fill__default);
      }
    }

    // Chrome
    &::-webkit-slider-thumb {
      @include thumb-base;

      -webkit-appearance: none;
    }

    &::-webkit-slider-runnable-track {
      height: 0.125rem;
    }

    // Firefox
    &::-moz-range-thumb {
      @include thumb-base;

      border: 0;
    }

    &:focus-visible {
      // Chrome
      &::-webkit-slider-thumb {
        @include focus-outside;
      }

      // Firefox
      &::-moz-range-thumb {
        @include focus-outside;
      }
    }
  }

  .a-tooltip {
    @include floating-outline;
    text-align: center;
    position: absolute;
    bottom: 2.25rem;
    left: calc(50% - 1.9375rem);
    position: absolute;
    text-align: center;
    visibility: hidden;
    white-space: nowrap;
  }

  label {
    font-size: 1rem;
    flex: 0 1 auto;
  }

  &.a-slider--labels-on-top {
    flex-wrap: wrap;

    // the labels should not shrink below 50%, but can grow as much as they want
    > label {
      order: 1;
      flex: 1 0 50%;
    }

    // the div containing the input should not shrink below 100%, and should be
    // the third element, visually
    // remove the margins around the input so that it aligns with the label's edges
    > div,
    > input {
      order: 3;
      flex: 1 0 100%;
      margin-left: 0;
      margin-right: 0;
    }

    // the label after the input container should be the second element (visually),
    // and the text should right-align
    > div + label,
    > input + label {
      order: 2;
      text-align: right;
    }

    > input {
      margin: 0.5rem 0;
    }
  }

  &--vertical {
    transform: rotate(270deg);

    &:has(label) {
      padding-right: 1.75rem;
    }

    label {
      position: absolute;
      left: 8.5625rem;
      top: -1.75rem;
      transform: rotate(90deg);
      transform-origin: left bottom;
    }

    .a-tooltip {
      bottom: -3.8rem;
      display: flex;
      transform: rotate(90deg);
    }
  }

  &--unitless .a-tooltip {
    left: calc(50% - 1.625rem);
  }
}