A dark device button that boots up on click: a glowing ring draws itself around the rim, the power icon lights in your accent and the glow keeps breathing while it is on. Click again to switch off.
Also known as: power button css, on off button animation, power toggle button.
<label class="btn-power">
<input class="btn-power-input" type="checkbox" aria-label="Power">
<span class="btn-power-face" aria-hidden="true">
<svg class="btn-power-ring" viewBox="0 0 48 48"><circle cx="24" cy="24" r="22"/><circle cx="24" cy="24" r="22"/></svg>
<svg class="btn-power-icon" viewBox="0 0 24 24"><path d="M12 3.5v7.5"/><path d="M7.2 6.6a7.4 7.4 0 1 0 9.6 0"/></svg>
</span>
</label>.btn-power {
--c: var(--btn-color, #3b82f6);
position: relative;
display: inline-grid;
place-items: center;
font: 600 var(--btn-size, 18px)/1.1 system-ui, sans-serif;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.btn-power-input {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
border-radius: 50%;
}
.btn-power-face {
position: relative;
display: grid;
place-items: center;
width: 3.8em;
height: 3.8em;
border-radius: 50%;
background: radial-gradient(circle at 50% 35%, color-mix(in srgb, var(--c) 14%, #2a3140), color-mix(in srgb, var(--c) 10%, #0d1117));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 0 0 1px rgba(148, 163, 184, 0.14),
0 0.35em 0.8em -0.2em rgba(2, 6, 23, 0.55),
0 0 0 0 transparent;
transition: transform 0.25s cubic-bezier(0.3, 1.6, 0.5, 1), box-shadow 0.5s ease;
}
.btn-power-ring {
position: absolute;
inset: 0.18em;
width: calc(100% - 0.36em);
height: calc(100% - 0.36em);
fill: none;
stroke: var(--c);
stroke-width: 2.5;
stroke-linecap: round;
transform: rotate(-90deg);
}
.btn-power-ring circle:first-child { stroke: rgba(148, 163, 184, 0.16); }
.btn-power-ring circle:last-child {
stroke-dasharray: 139;
stroke-dashoffset: 139;
transition: stroke-dashoffset 0.3s ease;
}
.btn-power-icon {
width: 1.5em;
height: 1.5em;
fill: none;
stroke: #64748b;
stroke-width: 2.4;
stroke-linecap: round;
transition: stroke 0.3s ease;
}
.btn-power:active .btn-power-face { transform: scale(0.93); }
.btn-power-input:focus-visible ~ .btn-power-face {
outline: 2px solid var(--c);
outline-offset: 0.2em;
}
.btn-power-input:checked ~ .btn-power-face {
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 0 0 1px color-mix(in srgb, var(--c) 40%, transparent),
0 0.35em 0.8em -0.2em rgba(2, 6, 23, 0.55),
0 0 1.2em 0.1em color-mix(in srgb, var(--c) 45%, transparent);
animation: btn-power-breathe 2.4s ease-in-out 0.8s infinite;
}
.btn-power-input:checked ~ .btn-power-face .btn-power-ring circle:last-child {
stroke-dashoffset: 0;
transition: stroke-dashoffset 0.7s cubic-bezier(0.65, 0, 0.35, 1);
}
.btn-power-input:checked ~ .btn-power-face .btn-power-icon {
stroke: color-mix(in srgb, var(--c) 70%, #fff);
animation: btn-power-flicker 0.6s steps(1) 0.1s;
}
@keyframes btn-power-breathe {
0%, 100% {
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 0 0 1px color-mix(in srgb, var(--c) 40%, transparent),
0 0.35em 0.8em -0.2em rgba(2, 6, 23, 0.55),
0 0 1.2em 0.1em color-mix(in srgb, var(--c) 45%, transparent);
}
50% {
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 0 0 1px color-mix(in srgb, var(--c) 40%, transparent),
0 0.35em 0.8em -0.2em rgba(2, 6, 23, 0.55),
0 0 0.6em 0 color-mix(in srgb, var(--c) 25%, transparent);
}
}
@keyframes btn-power-flicker {
0%, 30%, 60% { opacity: 1; }
15%, 45% { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
.btn-power, .btn-power *, .btn-power::before, .btn-power::after, .btn-power *::before, .btn-power *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--btn-color on the element or a parent.
Currently previewing #3b82f6.--btn-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 18px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.