Toggle Examples
The examples below assume that you have imported the component and set up the theme.
Basic
The host element itself is the button, with the slotted content as its label. It toggles on click, Enter, or Space.
<div class="toggle-basic"> <sv-toggle>Bold</sv-toggle></div>
<style> .toggle-basic { display: flex; justify-content: center; padding-block: 1em; }</style>Pressed and Disabled
pressed presses the toggle button from markup, and disabled blocks interaction in either state.
<div class="toggle-states"> <sv-toggle>Normal</sv-toggle> <sv-toggle disabled>Disabled</sv-toggle> <sv-toggle disabled pressed>Pressed Disabled</sv-toggle></div>
<style> .toggle-states { display: grid; gap: 1em; justify-items: center; padding-block: 1em; }</style>Custom Content
The default slot takes any markup: stack icons or labels, then animate or swap them with the :state(--pressed) selector.
Reduced Motion
Your system is set to reduce motion, so these animations will not play for you.
<div class="toggle-content-container"> <div class="variant"> <sv-toggle class="favorite-toggle" aria-label="Favorite" title="Favorite"> <!-- Outline star --> <svg class="icon outline" aria-hidden="true" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"> <path d="m354-287 126-76 126 77-33-144 111-96-146-13-58-136-58 135-146 13 111 97-33 143ZM233-120l65-281L80-590l288-25 112-265 112 265 288 25-218 189 65 281-247-149-247 149Zm247-350Z" ></path> </svg>
<!-- Filled star --> <svg class="icon filled" aria-hidden="true" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"> <path d="m233-120 65-281L80-590l288-25 112-265 112 265 288 25-218 189 65 281-247-149-247 149Z"></path> </svg> </sv-toggle> <span>animated icon</span> </div>
<div class="variant"> <sv-toggle class="mute-toggle"> <span class="icon-stack" aria-hidden="true"> <!-- Volume on --> <svg class="icon volume-on" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"> <path d="M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM400-606l-86 86H200v80h114l86 86v-252ZM300-480Z" ></path> </svg>
<!-- Volume off --> <svg class="icon volume-off" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"> <path d="M792-56 671-177q-25 16-53 27.5T560-131v-82q14-5 27.5-10t25.5-12L480-368v208L280-360H120v-240h128L56-792l56-56 736 736-56 56Zm-8-232-58-58q17-31 25.5-65t8.5-70q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 53-14.5 102T784-288ZM650-422l-90-90v-130q47 22 73.5 66t26.5 96q0 15-2.5 29.5T650-422ZM480-592 376-696l104-104v208Zm-80 238v-94l-72-72H200v80h114l86 86Zm-36-130Z" ></path> </svg> </span> Mute </sv-toggle> <span>icon and label</span> </div>
<div class="variant"> <sv-toggle class="follow-toggle"> <span class="follow">Follow</span> <span class="following">Following</span> </sv-toggle> <span>content swap</span> </div></div>
<style> .toggle-content-container { --anim-dur: 400ms; --anim-ease: cubic-bezier(0.486, 1.45, 0.2, 1);
display: grid; gap: 1.5em; justify-items: center;
.variant { display: grid; gap: 0.4em; justify-items: center;
> span { font-size: 0.8em; color: var(--sv-gray-3); } }
.icon { display: block; grid-area: 1 / 1; inline-size: 1.2em; block-size: 1.2em; fill: currentcolor; }
@media (prefers-reduced-motion: reduce) { --anim-dur: 0s; } }
.favorite-toggle { display: inline-grid; place-items: center; padding: 0.4em;
.icon { transition: scale var(--anim-dur) var(--anim-ease), rotate var(--anim-dur) var(--anim-ease); }
.icon.filled { rotate: -90deg; scale: 0; }
&:state(--pressed) { .icon.filled { rotate: 0deg; scale: 1; }
.icon.outline { rotate: 90deg; scale: 0; } } }
.mute-toggle { gap: 0.4em;
.icon-stack { display: grid; place-items: center; }
.icon { transition: opacity var(--anim-dur) ease, scale var(--anim-dur) var(--anim-ease); }
.icon.volume-off { opacity: 0; scale: 0.6; }
&:state(--pressed) { .icon.volume-on { opacity: 0; scale: 0.6; }
.icon.volume-off { opacity: 1; scale: 1; } } }
.follow-toggle { display: inline-grid; place-items: center;
/* Both labels share the same cell, so the button keeps the width of the widest one */ .follow, .following { grid-area: 1 / 1; }
.following { visibility: hidden; }
&:state(--pressed) { .follow { visibility: hidden; }
.following { visibility: visible; } } }</style>Styling
There are no parts to reach for: style the host element directly and pick the pressed look with :state(--pressed). The component tokens cover the common knobs, like --sv-toggle-active-bg for the pressed color.
<div class="toggle-styling"> <div class="variant"> <sv-toggle class="tinted" pressed>Approve</sv-toggle> <span>--sv-toggle-active-bg</span> </div>
<div class="variant"> <sv-toggle class="pill" pressed>Rounded</sv-toggle> <span>border-radius</span> </div>
<div class="variant"> <sv-toggle class="quiet">Quiet</sv-toggle> <span>background and border</span> </div></div>
<style> .toggle-styling { display: grid; gap: 1.5em; justify-items: center; padding-block: 1em;
.variant { display: grid; gap: 0.4em; justify-items: center;
span { font-size: 0.8em; color: var(--sv-gray-3); } }
.tinted { --sv-toggle-active-bg: light-dark(oklch(63% 0.1197 150deg), oklch(63% 0.105 150deg)); }
.pill { border-radius: calc(infinity * 1px); }
.quiet { background-color: transparent; border-color: transparent;
&:state(--pressed) { color: var(--sv-accent); background-color: color-mix(in srgb, var(--sv-accent) 12%, transparent); } } }</style>