Skip to content

Toggle Switch Theme Toggle

A sun in the thumb slot turns into a moon as the switch flips, and the thumb stretches while pressed. The card follows the switch through :has(:state(--checked)), no JavaScript involved.

Reduced Motion

Your system is set to reduce motion, so the animations shown on this page will not play for you.

Dark mode

Easier on the eyes at night.

Code

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

Theme Toggle
<div class="theme-toggle">
21 collapsed lines
<div class="card">
<div>
<strong>Dark mode</strong>
<p>Easier on the eyes at night.</p>
</div>
<sv-toggle-switch class="theme-switch" aria-label="Dark mode">
<span class="icons" slot="thumb">
<!-- Sun -->
<svg class="sun" aria-hidden="true" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
</svg>
<!-- Moon -->
<svg class="moon" aria-hidden="true" viewBox="0 0 24 24">
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
</svg>
</span>
</sv-toggle-switch>
</div>
</div>
<style>
102 collapsed lines
.theme-toggle {
display: grid;
justify-items: center;
padding-block: 1em;
.card {
display: flex;
gap: 1.5em;
align-items: center;
padding: 1em 1.25em;
color: #26262b;
background-color: #fff;
border: 1px solid #e2e2e8;
border-radius: var(--sv-bdr-rad-lg);
transition: 500ms ease;
transition-property: color, background-color, border-color;
p {
margin: 0.15em 0 0;
font-size: 0.85em;
opacity: 0.7;
}
}
&:has(.theme-switch:state(--checked)) .card {
color: #e7e7ec;
background-color: #18181d;
border-color: #2e2e36;
}
}
.theme-switch {
--sv-toggle-switch-bg: #e4e4e9;
--sv-toggle-switch-active-bg: #3a3a52;
--sv-toggle-switch-thumb-bg: #fff;
--sv-toggle-switch-active-clr: #fff;
--sv-toggle-switch-sz: 1.75em;
--sv-toggle-switch-width: 3.6em;
--sv-toggle-switch-dur: 450ms;
--sv-toggle-switch-ease: cubic-bezier(0.34, 1.3, 0.64, 1);
--stretch-dur: 200ms;
&::part(background) {
display: none;
}
&::part(thumb) {
box-shadow: 0 1px 3px rgb(0 0 0 / 20%);
transition:
transform var(--sv-toggle-switch-dur) var(--sv-toggle-switch-ease),
inline-size var(--stretch-dur) ease;
}
/* The thumb stretches toward the center while pressed */
&:active {
--sv-toggle-switch-thumb-width: 2.1em;
}
.icons {
display: grid;
place-items: center;
block-size: 100%;
}
svg {
grid-area: 1 / 1;
inline-size: 60%;
fill: none;
stroke: currentcolor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
transition: var(--sv-toggle-switch-dur) cubic-bezier(0.65, 0, 0.35, 1);
transition-property: rotate, scale, opacity;
}
.sun {
--hidden-rotate: 90deg;
color: #f59e0b;
}
.moon {
--hidden-rotate: -90deg;
color: #6366f1;
}
/* Hidden icon */
&:not(:state(--checked)) .moon,
&:state(--checked) .sun {
opacity: 0;
rotate: var(--hidden-rotate);
scale: 0.5;
}
@media (prefers-reduced-motion: reduce) {
--sv-toggle-switch-dur: 0s;
--stretch-dur: 0s;
}
}
</style>