Slider
<quiet-slider>
Sliders let users select numeric values within a given range by moving a thumb along a track.
<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.
<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.
<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.
<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.
<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.
<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>
<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.
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
|
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'
|
'horizontal'
|
size
|
The slider's size. |
|
'xs'
|
'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'
|
'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.