While the cursor rests on it, a soft halo in the accent color swells and fades like slow breathing. The halo is a blurred pseudo-element, so only its opacity and transform animate.
Also known as: pulsing glow button, glowing button animation, glow pulse hover.
<button class="btn-breathing-glow">Start listening</button>
.btn-breathing-glow {
--c: var(--btn-color, #3b82f6);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5em;
font: 600 var(--btn-size, 18px)/1.2 system-ui, sans-serif;
cursor: pointer;
position: relative;
isolation: isolate;
padding: 0.8em 1.8em;
border: 0;
border-radius: 2em;
background: transparent;
color: #fff;}
.btn-breathing-glow::before,
.btn-breathing-glow::after {
content: "";
position: absolute;
inset: 0;
z-index: -1;
border-radius: inherit;
background: var(--c);
}
.btn-breathing-glow::before {
filter: blur(0.9em);
opacity: 0;
transform: scale(0.9);
transition: opacity 0.4s ease, transform 0.4s ease;
}
.btn-breathing-glow:is(:hover, :focus-visible)::before {
animation:
btn-breathing-glow-in 0.5s ease-out both,
btn-breathing-glow-breathe 2.6s ease-in-out 0.5s infinite;
}
@keyframes btn-breathing-glow-in {
to { opacity: 0.55; transform: scale(1); }
}
@keyframes btn-breathing-glow-breathe {
0%, 100% { opacity: 0.55; transform: scale(1); }
50% { opacity: 0.95; transform: scale(1.15, 1.35); }
}
@media (prefers-reduced-motion: reduce) {
.btn-breathing-glow, .btn-breathing-glow *, .btn-breathing-glow::before, .btn-breathing-glow::after, .btn-breathing-glow *::before, .btn-breathing-glow *::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.