Skip to content

Slider Volume

Use a vertical slider with custom thumb and tooltip slots and a mute control.

Code

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

Volume Slider
<div class="slider-wrapper">
49 collapsed lines
<div class="slider-container">
<!-- Button that toggles mute and displays the correct volume icon -->
<button type="button" class="toggle-mute-btn" data-state="volume-medium" title="Toggle mute">
<!-- Icon: Full volume -->
<svg class="icon volume-full" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path
d="M561.54-155.62v-62q86.54-27.53 139.42-100 52.89-72.46 52.89-163.38t-52.89-163.38q-52.88-72.47-139.42-100v-62q111.69 29.92 182 119.92 70.3 90 70.3 205.46 0 115.46-70.3 205.46-70.31 90-182 119.92ZM146.16-380v-200h148.46l171.53-171.53v543.06L294.62-380H146.16Zm415.38 46.15v-294.3q40.46 22 62.54 61.96 22.07 39.96 22.07 86.19 0 45.61-22.27 84.88-22.27 39.27-62.34 61.27Z"
></path>
</svg>
<!-- Icon: Muted -->
<svg class="icon volume-off" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path
d="M778.92-74.46 658.31-195.08q-20.77 13.31-43.77 23.08-23 9.77-47.62 16.38v-62q12.85-4.61 25-9.42 12.16-4.81 23-11.42L471.54-381.85v173.38L300-380H151.54v-200h121.85L79.85-773.54 122-815.69l699.07 699.07-42.15 42.16Zm-19.54-216.23-43-43q20.85-32.54 31.85-69.81t11-77.5q0-90.92-52.88-163.38-52.89-72.47-139.43-100v-62Q679-776.46 749.11-686.46q70.12 90 70.12 205.46 0 52.62-15.65 101.04-15.66 48.42-44.2 89.27ZM636.92-413.15l-70-70v-145q40.46 22 62.54 61.96T651.54-480q0 17.69-3.66 34.5-3.65 16.81-10.96 32.35ZM471.54-578.54l-86.31-86.69 86.31-86.3v172.99Z"
></path>
</svg>
<!-- Icon: Low volume -->
<svg class="icon volume-low" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path d="M300-380v-200h148.46L620-751.53v543.06L448.46-380H300Z"></path>
</svg>
<!-- Icon: Medium volume -->
<svg class="icon volume-medium" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path
d="M220-380v-200h148.46L540-751.53v543.06L368.46-380H220Zm415.38 46.15v-294.3q38.85 21 61.73 60.96Q720-527.23 720-480t-22.89 86.19q-22.88 38.96-61.73 59.96Z"
></path>
</svg>
</button>
<!-- Vertical volume slider component -->
<sv-slider class="volume-slider" aria-label="Volume" max="150" thumb-contained value="50" vertical>
<!-- Tooltip showing the current slider value -->
<div aria-hidden="true" slot="tooltip">
<strong>
<sv-text-flow data-value></sv-text-flow>
</strong>
</div>
<!-- Custom thumb replacing the default slider thumb -->
<div class="custom-thumb" aria-hidden="true" slot="thumb-1">
<svg class="icon" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
<path
d="M400-120q-66 0-113-47t-47-113q0-66 47-113t113-47q23 0 42.5 5.5T480-418v-422h240v160H560v400q0 66-47 113t-113 47Z"
></path>
</svg>
</div>
</sv-slider>
</div>
</div>
<script type="application/x-typescript" module>
16 collapsed lines
const slider = document.querySelector<Slider>(".volume-slider")!;
const muteToggleBtn = document.querySelector<HTMLButtonElement>(".toggle-mute-btn")!;
// Update icon when slider value changes
slider.addEventListener("valuechange", () => {
const value = slider.value as number;
muteToggleBtn.dataset.state =
value === slider.min ? "volume-off" : value < 50 ? "volume-low" : value < 100 ? "volume-medium" : "volume-full";
});
// Toggle mute/unmute by clicking the button
muteToggleBtn.addEventListener("click", () => {
const value = slider.value === slider.min ? slider.max / 2 : slider.min;
slider.setValue({ value, animate: true });
});
</script>
<style>
114 collapsed lines
.slider-wrapper {
--accent-color: #dde1ff;
--accent-text-color: #1a1b23;
display: flex;
flex-direction: column;
gap: 1em;
align-items: center;
padding: 2em;
background: linear-gradient(135deg, #c3c8ef, #4b5185);
border-radius: 4px;
}
.slider-container {
display: flex;
flex-direction: column;
gap: 1em;
align-items: center;
padding: 6px;
background-color: #1f2024;
border-radius: 2em;
box-shadow: 0 0 0.5em #00000030;
transition: scale 350ms cubic-bezier(0.34, 1.5, 0.64, 1);
}
/* Slight scale effect when the slider is actively used */
.slider-container:has(.volume-slider:state(--active)) {
scale: 1.05;
}
.volume-slider {
--sv-slider-track-thickness: 2em;
--sv-slider-thumb-sz: 2em;
--sv-slider-bdr-rad: 2em;
--sv-slider-tooltip-offset: 2em;
--sv-slider-fill-edge-stops: 0;
--sv-slider-fill-clr: var(--accent-color);
/* Required height for vertical slider */
min-block-size: 200px;
cursor: grab;
&:state(--active) {
cursor: grabbing;
}
/* Slider track styling */
&::part(track) {
background: #44464f;
border: none;
}
/* Tooltip styling */
&::part(tooltip) {
min-inline-size: 1.8em;
color: var(--accent-text-color);
background-color: var(--accent-color);
border-color: transparent;
box-shadow: 0 0 0.5em #00000030;
}
&::part(tooltip-arrow) {
fill: var(--accent-color);
stroke: transparent;
}
/* Custom thumb appearance */
.custom-thumb {
display: grid;
place-items: center;
inline-size: var(--sv-slider-thumb-sz);
block-size: var(--sv-slider-thumb-sz);
color: var(--accent-text-color);
background-color: var(--accent-color);
border-radius: var(--sv-slider-bdr-rad);
}
}
.icon {
inline-size: 1.4em;
block-size: 1.4em;
fill: currentcolor;
}
.toggle-mute-btn {
display: grid;
place-items: center;
inline-size: 2.5em;
block-size: 2.5em;
color: var(--accent-text-color);
cursor: pointer;
background-color: var(--accent-color);
border: none;
border-radius: 50%;
.icon {
display: none;
inline-size: 1.8em;
block-size: 1.8em;
}
&[data-state="volume-off"] .volume-off,
&[data-state="volume-low"] .volume-low,
&[data-state="volume-medium"] .volume-medium,
&[data-state="volume-full"] .volume-full {
display: block;
}
}
@media (prefers-reduced-motion: reduce) {
.slider-container {
transition-duration: 0s;
}
}
</style>