Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

    Components

    Range

    Full supportSupported since v125.Full supportSupported since v128.Full supportSupported since v18.

    Anatomy

    • label.ui-range

      Container element.

    • .ui-label

      The label for the range.

    • <output>

      Shows the current value, with an optional valueSuffix.

    • .ui-start-text

      Description text displayed above the input.

    • <input>

      The range input.

    • .ui-end-text

      Supporting text displayed below the input.

    <label class="ui-range">
    <span class="ui-label" id="range-default-1-label">Label</span>
    <span class="ui-start-text" id="range-default-1-start-text">Min</span>
    <input
    aria-describedby="range-default-1-start-text"
    aria-labelledby="range-default-1-label"
    type="range"
    />
    </label>

    Start text & End text

    <label class="ui-range">
    <span class="ui-label" id="range-start-end-1-label">Label</span>
    <span class="ui-start-text" id="range-start-end-1-start-text"
    >Start helper text</span
    >
    <input
    aria-describedby="range-start-end-1-start-text range-start-end-1-end-text"
    aria-labelledby="range-start-end-1-label"
    type="range"
    />
    <span class="ui-end-text" id="range-start-end-1-end-text"
    >End helper text</span
    >
    </label>

    Value

    Add an <output class="ui-value"> sibling to the.ui-label with for pointing at the input's id. Optionally set data-suffix for a unit (e.g. °,px). Updating its text content is the consumer's responsibility.

    <label class="ui-range">
    <span class="ui-label" id="range-value-1-label">Hue</span>
    <output class="ui-value" for="hueRange" data-suffix="°">250°</output>
    <input
    aria-labelledby="range-value-1-label"
    type="range"
    id="hueRange"
    min="0"
    max="360"
    value="250"
    />
    </label>

    Tick marks

    Use the list attribute on the <input> and follow it with a <datalist> element containing<option> elements with value andlabel attributes.

    <label class="ui-range">
    <span class="ui-label" id="range-tick-marks-1-label"
    >Tick marks with labels</span
    >
    <input
    aria-labelledby="range-tick-marks-1-label"
    list="labeled-markers"
    type="range"
    />
    <datalist id="labeled-markers">
    <option value="0" label="0%"></option>
    <option value="25" label="25%"></option>
    <option value="50" label="50%"></option>
    <option value="75" label="75%"></option>
    <option value="100" label="100%"></option>
    </datalist>
    </label>

    Variants

    Use the .ui-filled, .ui-default, or .ui-tonalclass to swap the track surface for better contrast on different backgrounds.

    <label class="ui-range">
    <span class="ui-label" id="range-surfaces-1-label">Default</span>
    <input aria-labelledby="range-surfaces-1-label" type="range" />
    </label>
    <label class="ui-range ui-default">
    <span class="ui-label" id="range-surfaces-2-label"
    ><code>default</code> = <code>var(--surface-default)</code></span
    >
    <input aria-labelledby="range-surfaces-2-label" type="range" />
    </label>
    <label class="ui-range ui-filled">
    <span class="ui-label" id="range-surfaces-3-label"
    ><code>filled</code> = <code>var(--surface-filled)</code></span
    >
    <input aria-labelledby="range-surfaces-3-label" type="range" />
    </label>
    <label class="ui-range ui-tonal">
    <span class="ui-label" id="range-surfaces-4-label"
    ><code>tonal</code> = <code>var(--surface-tonal)</code></span
    >
    <input aria-labelledby="range-surfaces-4-label" type="range" />
    </label>

    Disabled

    <label class="ui-range">
    <span class="ui-label" id="range-disabled-1-label">Disabled</span>
    <input aria-labelledby="range-disabled-1-label" type="range" disabled />
    </label>

    Validation

    <label class="ui-range" data-invalid>
    <span class="ui-label" id="range-validation-1-label">Invalid Range</span>
    <input
    aria-describedby="range-validation-1-end-text"
    aria-labelledby="range-validation-1-label"
    type="range"
    />
    <span class="ui-end-text" id="range-validation-1-end-text"
    >This value is incorrect.</span
    >
    </label>

    Spread

    <label class="ui-range ui-spread">
    <span class="ui-label" id="range-row-1-label">Spread Layout</span>
    <span class="ui-start-text" id="range-row-1-start-text">Start text</span>
    <input
    aria-describedby="range-row-1-start-text range-row-1-end-text"
    aria-labelledby="range-row-1-label"
    type="range"
    />
    <span class="ui-end-text" id="range-row-1-end-text">End text</span>
    </label>
    <label class="ui-range ui-spread">
    <span class="ui-label" id="range-row-2-label">Disabled</span>
    <span class="ui-start-text" id="range-row-2-start-text">Start text</span>
    <input
    aria-describedby="range-row-2-start-text range-row-2-end-text"
    aria-labelledby="range-row-2-label"
    type="range"
    disabled
    />
    <span class="ui-end-text" id="range-row-2-end-text">End text</span>
    </label>
    <label class="ui-range ui-spread" data-invalid>
    <span class="ui-label" id="range-row-3-label">Invalid Range</span>
    <span class="ui-start-text" id="range-row-3-start-text">Start text</span>
    <input
    aria-describedby="range-row-3-start-text range-row-3-end-text"
    aria-labelledby="range-row-3-label"
    type="range"
    />
    <span class="ui-end-text" id="range-row-3-end-text"
    >This value is incorrect.</span
    >
    </label>
    <label class="ui-range ui-spread">
    <span class="ui-label" id="range-row-4-label">Tick marks with labels</span>
    <input
    aria-labelledby="range-row-4-label"
    list="labeled-markers-spread"
    type="range"
    />
    <datalist id="labeled-markers-spread">
    <option value="0" label="0%"></option>
    <option value="25" label="25%"></option>
    <option value="50" label="50%"></option>
    <option value="75" label="75%"></option>
    <option value="100" label="100%"></option>
    </datalist>
    </label>

    Disabled

    <label class="ui-range ui-spread">
    <span class="ui-label" id="range-row-disabled-label">Volume</span>
    <input
    type="range"
    aria-labelledby="range-row-disabled-label"
    min="0"
    max="100"
    value="50"
    disabled
    />
    </label>

    Validation

    <label class="ui-range ui-spread" data-invalid>
    <span class="ui-label" id="range-row-validation-label">Volume</span>
    <input
    type="range"
    aria-labelledby="range-row-validation-label"
    min="0"
    max="100"
    value="50"
    />
    </label>

    Accessibility

    • Right Arrow: Increase the value of the slider by one step.
    • Up Arrow: Increase the value of the slider by one step.
    • Left Arrow: Decrease the value of the slider by one step.
    • Down Arrow: Decrease the value of the slider by one step.
    • Home: Set the slider to the first allowed value in its range.
    • End: Set the slider to the last allowed value in its range.
    • Page Up (Optional): Increase the slider value by an amount larger than the step change made by Up Arrow.
    • Page Down (Optional): Decrease the slider value by an amount larger than the step change made by Down Arrow.

    API

    Range API

    TypeModifiersDefaultDescription
    Layout.ui-spread-Pushes the label and description to one side and the input to the other.
    Validation[data-invalid]-Shows error styles.
    Variants.ui-default, .ui-filled, .ui-tonal-The variant to use.

    Parts

    PartDescription
    label.ui-rangeContainer element.
    .ui-labelThe label for the range.
    <output>Shows the current value, with an optional valueSuffix.
    .ui-start-textDescription text displayed above the input.
    <input>The range input.
    .ui-end-textSupporting text displayed below the input.

    Add a <datalist> after the input for tick marks.

    Browser support

    Full supportSupported since v125.Full supportSupported since v128.Full supportSupported since v18.

    See also the full browser support guide.

    Installation

    @layer components.root {
    :where(.ui-range) {
    --_motion: var(--motion, 1);
    display: grid;
    gap: var(--size-1) 0;
    grid-template-columns: 1fr auto;
    position: relative;
    .ui-label {
    color: var(--text-primary);
    font-size: var(--font-size-05);
    font-weight: 600;
    grid-column: 1;
    grid-row: 1;
    }
    :where(.ui-value) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    grid-column: 2;
    grid-row: 1;
    line-height: 1.5;
    text-align: end;
    }
    .ui-start-text {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    grid-column: 1/-1;
    grid-row: 2;
    line-height: 1.5;
    }
    :where(.ui-end-text) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    grid-column: 1/-1;
    grid-row: 5;
    line-height: 1.5;
    }
    /* Datalist (tick mark labels) */
    :where(datalist) {
    color: var(--text-muted);
    display: flex;
    font-size: var(--font-size-0);
    grid-column: 1/-1;
    grid-row: 4;
    justify-content: space-between;
    margin-top: calc(var(--size-1) * -1);
    padding-inline: calc(3ex / 2);
    & > option {
    display: flex;
    inline-size: 0;
    justify-content: center;
    padding: 0;
    white-space: nowrap;
    }
    }
    /* Disabled */
    &:has([disabled]) {
    cursor: not-allowed;
    opacity: 0.64;
    user-select: none;
    .ui-label,
    .ui-value,
    .ui-start-text,
    .ui-end-text {
    cursor: not-allowed;
    }
    }
    /* Color */
    &.ui-filled input[type="range"] {
    --_track-color: var(--surface-filled);
    }
    &.ui-default input[type="range"] {
    --_track-color: var(--surface-default);
    }
    &.ui-tonal input[type="range"] {
    --_track-color: var(--surface-tonal);
    }
    /* Orientation */
    &.ui-spread {
    align-items: center;
    column-gap: var(--size-4);
    container-type: inline-size;
    grid-template-columns: 1fr auto;
    .ui-label {
    grid-column: 1;
    grid-row: 1;
    }
    .ui-start-text {
    color: var(--text-muted);
    display: block;
    font-size: var(--font-size-0);
    grid-column: 1;
    grid-row: 2;
    line-height: 1.5;
    }
    input[type="range"] {
    align-self: center;
    grid-column: 2;
    grid-row: 1;
    min-inline-size: 25ch;
    }
    :where(.ui-value) {
    grid-column: 2;
    grid-row: 2;
    }
    :where(.ui-end-text) {
    grid-column: 2;
    grid-row: 2;
    text-align: end;
    }
    datalist {
    grid-column: 2;
    grid-row: 2;
    }
    &:has(datalist) :where(.ui-end-text) {
    grid-row: 3;
    }
    &:has(.ui-value) :where(.ui-end-text) {
    grid-row: 3;
    }
    &:has(.ui-value):has(datalist) :where(.ui-end-text) {
    grid-row: 4;
    }
    @container (width < 400px) {
    grid-template-columns: 1fr;
    * {
    grid-column: 1/-1;
    }
    .ui-label {
    grid-row: 1;
    }
    :where(.ui-value) {
    grid-row: 1;
    justify-self: end;
    text-align: end;
    }
    .ui-start-text {
    grid-row: 2;
    }
    input[type="range"] {
    grid-column: 1/-1;
    grid-row: 3;
    }
    datalist {
    grid-column: 1/-1;
    grid-row: 4;
    }
    :where(.ui-end-text) {
    grid-row: 5;
    text-align: start;
    }
    }
    }
    /* Validation */
    &[data-invalid],
    &:has(:user-invalid) {
    --_thumb-bg: var(--color-9);
    --_thumb-highlight-color: oklch(from var(--color-9) 70% 100% h / 20%);
    .ui-start-text,
    .ui-end-text,
    .ui-value {
    color: var(--color-9);
    }
    }
    /* Range input */
    :where(& > input[type="range"]) {
    --_thumb-bg: var(--primary);
    --_thumb-highlight-color: oklch(from var(--primary) 70% 100% h / 20%);
    --_thumb-highlight-size: 0px;
    --_thumb-offset: -1.125ex;
    --_thumb-size: 3ex;
    --_track-color: var(--field-border-color);
    --_track-fill: 0%;
    --_track-fill-color: var(--primary);
    --_track-height: 0.75ex;
    --_track-radius: 1e5px;
    appearance: none;
    background: transparent;
    block-size: var(--size-4);
    display: block;
    grid-column: 1/-1;
    grid-row: 3;
    inline-size: 100%;
    outline-offset: 1ex;
    @media (hover: none) {
    --_thumb-offset: -11.5px;
    --_thumb-size: 30px;
    }
    /* Track */
    &::-webkit-slider-runnable-track {
    appearance: none;
    background: linear-gradient(
    to right,
    var(--_track-fill-color) var(--_track-fill, 0%),
    var(--_track-color) 0%
    );
    block-size: var(--_track-height);
    border-radius: var(--_track-radius);
    }
    &::-moz-range-track {
    appearance: none;
    background: var(--_track-color);
    block-size: var(--_track-height);
    border-radius: var(--_track-radius);
    }
    &::-moz-range-progress {
    appearance: none;
    background: var(--_track-fill-color);
    block-size: var(--_track-height);
    border-radius: var(--_track-radius);
    }
    /* Ring */
    &::-webkit-slider-thumb {
    appearance: none;
    background: var(--_thumb-bg);
    block-size: var(--_thumb-size);
    border: 3px solid var(--surface-default);
    border-radius: 50%;
    box-shadow: 0 0 0 var(--_thumb-highlight-size)
    var(--_thumb-highlight-color);
    cursor: ew-resize;
    inline-size: var(--_thumb-size);
    margin-block-start: var(--_thumb-offset);
    transition: box-shadow calc(0.1s * var(--_motion)) ease;
    .ui-fieldset-item:focus-within & {
    border-color: var(--gray-14);
    }
    }
    &::-moz-range-thumb {
    appearance: none;
    background: var(--_thumb-bg);
    block-size: var(--_thumb-size);
    border: 3px solid var(--surface-default);
    border-radius: 50%;
    box-shadow: 0 0 0 var(--_thumb-highlight-size)
    var(--_thumb-highlight-color);
    cursor: ew-resize;
    inline-size: var(--_thumb-size);
    margin-block-start: var(--_thumb-offset);
    transition: box-shadow calc(0.1s * var(--_motion)) ease;
    .ui-fieldset-item:focus-within & {
    border-color: var(--gray-14);
    }
    }
    /* Element state */
    .ui-range:has(:user-invalid) &,
    .ui-range[data-invalid] & {
    --_track-fill-color: var(--color-9);
    }
    &:not([disabled]) {
    &:hover {
    --_thumb-highlight-size: var(--size-1);
    }
    &:active {
    --_thumb-highlight-size: var(--size-2);
    --_track-color: light-dark(
    oklch(from var(--field-border-color) calc(l * 0.9) c h),
    oklch(from var(--field-border-color) calc(l * 1.1) c h)
    );
    }
    }
    &[disabled] {
    --_thumb-bg: oklch(from var(--text-primary) l c h / 50%);
    --_track-color: var(--field-border-color);
    cursor: not-allowed;
    &::-webkit-slider-thumb {
    cursor: not-allowed;
    }
    &::-moz-range-thumb {
    cursor: not-allowed;
    }
    }
    }
    }
    }