Skip to content

Toggle Custom Content

The default slot accepts any content: stack icons or labels, then animate or swap them with the :state(--pressed) CSS state. Give icon-only toggle buttons a title and an aria-label.

Mute

Code

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

Toggle Custom Content
<div class="toggle-content-container">
<!-- Animated icon -->
<sv-toggle class="favorite-toggle" aria-label="Favorite" title="Favorite">
9 collapsed lines
<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>
<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>
<!-- Icon and label -->
<sv-toggle class="mute-toggle">
13 collapsed lines
<span class="icon-stack" aria-hidden="true">
<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>
<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>
<!-- Content swap -->
<sv-toggle class="follow-toggle">
<span class="follow">Follow</span>
<span class="following">Following</span>
</sv-toggle>
</div>
<style>
.toggle-content-container {
19 collapsed lines
--anim-dur: 400ms;
--anim-ease: cubic-bezier(0.486, 1.45, 0.2, 1);
display: flex;
flex-direction: column;
gap: 1em;
align-items: center;
.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 {
14 collapsed lines
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 {
17 collapsed lines
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 {
12 collapsed lines
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>