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">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>