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.
<div class="slider-wrapper"> <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 -->5 collapsed lines
<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 -->5 collapsed lines
<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 -->3 collapsed lines
<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 -->5 collapsed lines
<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">5 collapsed lines
<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="module"> /** @type {Slider} */ const slider = document.querySelector(".volume-slider");
/** @type {HTMLDivElement} */ const muteToggleBtn = document.querySelector(".toggle-mute-btn");
// Update icon when slider value changes slider.addEventListener("valuechange", event => { setIconState(event.detail.value); });
// Toggle mute/unmute by clicking the button muteToggleBtn.addEventListener("click", () => { const currentValue = slider.value; if (Array.isArray(currentValue)) return;
const value = currentValue === slider.min ? slider.max / 2 : slider.min; slider.setValue({ value, animate: true, }); setIconState(value); });
/** @param {number} value */ function setIconState(value) { if (value === 0) { muteToggleBtn.dataset.state = "volume-off"; return; }
if (value < 50) { muteToggleBtn.dataset.state = "volume-low"; return; }
if (value < 100) { muteToggleBtn.dataset.state = "volume-medium"; return; }
muteToggleBtn.dataset.state = "volume-full"; }</script>
<style>23 collapsed lines
.slider-wrapper { display: flex; flex-direction: column; gap: 1em; align-items: center; padding: 2em; background: linear-gradient(135deg, #ff6a00, #7a00ff); border-radius: 4px; }
.slider-container { display: flex; flex-direction: column; gap: 1em; align-items: center; padding: 6px; background-color: var(--sv-gray-6); 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-thickness: 2em; --sv-thumb-size: 2em; --sv-bdr-rad-md: 2em; --sv-tooltip-offset: 2em;
/* Required height for vertical slider */ min-block-size: 200px; cursor: grab;
&:state(--active) { cursor: grabbing; }
/* Slider track styling */ &::part(track) { background: var(--sv-gray-5); border: none; }
/* Tooltip styling */ &::part(tooltip) { min-inline-size: 1.8em; color: var(--sv-accent-content); background-color: var(--sv-accent); border-color: transparent; box-shadow: 0 0 0.5em #00000030; }
&::part(tooltip-arrow) { fill: var(--sv-accent); stroke: transparent; }
/* Custom thumb appearance */ .custom-thumb { display: flex; align-items: center; justify-content: center; inline-size: var(--sv-thumb-size); block-size: var(--sv-thumb-size); background-color: var(--sv-accent); border-radius: var(--sv-bdr-rad-md); }39 collapsed lines
}
.icon { inline-size: 1.4em; block-size: 1.4em; fill: var(--sv-accent-content); }
.toggle-mute-btn { display: grid; place-items: center; inline-size: 2.5em; block-size: 2.5em; color: var(--sv-accent-content); cursor: pointer; background-color: var(--sv-accent); border: none; border-radius: 500px;
.icon { display: none; }
&[data-state="volume-medium"] .icon.volume-medium { display: block; }
&[data-state="volume-full"] .icon.volume-full { display: block; }
&[data-state="volume-off"] .icon.volume-off { display: block; }
&[data-state="volume-low"] .icon.volume-low { display: block; } }
@media (prefers-reduced-motion: reduce) { .slider-container { transition-duration: 0s; } }</style>