Skip to content

Slider Multiple Thresholds

Assign any number of ordered values to value and customize each thumb with its one-based indexed slot and CSS part. The shared tooltip follows the thumb being dragged, while labels remain tied to their thumb when values move.

Cool16 °C Ideal22 °C Warm28 °C

Code

The following example assumes that you have imported the component and set up the theme.

Multiple Thresholds
<div class="threshold-example">
<div class="threshold-heading">
<label for="comfort-thresholds">Comfort thresholds</label>
<div class="threshold-values">
<span>
<i class="cool" aria-hidden="true"></i>
Cool
<output data-value-index="0">16 °C</output>
</span>
<span>
<i class="ideal" aria-hidden="true"></i>
Ideal
<output data-value-index="1">22 °C</output>
</span>
<span>
<i class="warm" aria-hidden="true"></i>
Warm
<output data-value-index="2">28 °C</output>
</span>
</div>
</div>
<sv-slider id="comfort-thresholds" class="threshold-slider" invert-tooltip max="40" value="16 22 28">
<span class="threshold-thumb" aria-hidden="true" slot="thumb-1"></span>
<span class="threshold-thumb" aria-hidden="true" slot="thumb-2"></span>
<span class="threshold-thumb" aria-hidden="true" slot="thumb-3"></span>
<span aria-label="Cool limit" slot="thumb-label-1"></span>
<span aria-label="Ideal temperature" slot="thumb-label-2"></span>
<span aria-label="Warm limit" slot="thumb-label-3"></span>
<span aria-hidden="true" slot="tooltip">
<span data-value></span>
°C
</span>
</sv-slider>
</div>
<script type="module">
/** @type {HTMLElement} */
const example = document.querySelector(".threshold-example");
/** @type {Slider} */
const slider = example.querySelector(".threshold-slider");
/** @type {NodeListOf<HTMLOutputElement>} */
const outputs = example.querySelectorAll("[data-value-index]");
const descriptions = ["lower boundary", "preferred temperature", "upper boundary"];
slider.valueLabelHandler = (value, index) => {
return `${value} degrees Celsius, ${descriptions[index]}`;
};
updateOutputs();
slider.addEventListener("valuechange", updateOutputs);
function updateOutputs() {
const values = slider.value;
if (!Array.isArray(values)) return;
for (const [index, output] of outputs.entries()) {
output.value = `${values[index]} °C`;
}
}
</script>
57 collapsed lines
<style>
.threshold-example {
display: grid;
gap: 1.75rem;
max-inline-size: 42rem;
padding: clamp(1rem, 4vw, 2rem);
margin: auto;
background: var(--sv-gray-7);
border: 1px solid var(--sv-gray-5);
border-radius: var(--sv-bdr-rad-lg);
}
.threshold-heading {
display: grid;
gap: 0.8rem;
> label {
font-weight: 700;
}
}
.threshold-values {
display: flex;
flex-wrap: wrap;
gap: 0.75rem 1.25rem;
font-size: 0.85rem;
color: var(--sv-gray-3);
span {
display: inline-flex;
gap: 0.4rem;
align-items: center;
}
i {
inline-size: 0.65rem;
block-size: 0.65rem;
background: currentcolor;
border-radius: 50%;
}
output {
font-weight: 700;
color: var(--sv-gray-1);
}
.cool {
color: #3b82f6;
}
.ideal {
color: #22c55e;
}
.warm {
color: #f97316;
}
}
.threshold-slider {
--sv-thickness: 0.8rem;
--sv-thumb-size: 1.65rem;
--sv-tooltip-offset: 1.5rem;
inline-size: 100%;
&::part(track) {
background: linear-gradient(90deg, #3b82f6, #22c55e, #f97316);
}
&:dir(rtl)::part(track) {
background: linear-gradient(270deg, #3b82f6, #22c55e, #f97316);
}
&::part(fill) {
background: rgb(255 255 255 / 35%);
}
&::part(thumb-1) {
color: #3b82f6;
}
&::part(thumb-2) {
color: #22c55e;
}
&::part(thumb-3) {
color: #f97316;
}
&::part(tooltip) {
background: transparent;
border-color: currentcolor;
backdrop-filter: blur(7px);
}
&::part(tooltip-arrow) {
fill: currentcolor;
stroke: currentcolor;
}
9 collapsed lines
}
.threshold-thumb {
inline-size: var(--sv-thumb-size);
block-size: var(--sv-thumb-size);
background: currentcolor;
border: 3px solid var(--sv-gray-7);
border-radius: 50%;
box-shadow: 0 2px 5px rgb(0 0 0 / 30%);
}
</style>