Glow Pulse
An indigo halo that breathes in a loop (20 → 40 px every 2 s) and freezes wider on hover — to point at the one action to take now.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Buttons has 27 effects, including 2 free. Explore the category →
3 usage examples



How it works
@keyframes glowPulse animates a single property: box-shadow. At 0% and 100%, 0 0 20px rgba(99,102,241,.4); at 50%, 0 0 40px rgba(99,102,241,.8) — blur radius and opacity both double mid-cycle. The halo color is the button's own background (#6366f1): it reads as light emitted by the button, not as a shadow.
.btn-glow applies it with animation: glowPulse 2s ease-in-out infinite (the shipped code writes it in serialized form, 2s ease-in-out 0s infinite normal none running glowPulse — the same declaration). ease-in-out both ways gives a symmetrical breathing with no jerk at the extremes. The animation starts on load, with no trigger.
On :hover, animation: none (serialized auto ease 0s 1 normal none running none) removes the loop and a static, opaque, wider box-shadow: 0 0 50px #6366f1 replaces it. No transition is declared on box-shadow: the switch is instant, the halo snaps to 50 px; when the cursor leaves, the animation restarts at its 0% (20 px), just as instantly.
Rendering cost: box-shadow is not a GPU-composited property — every frame repaints the button area extended by the blur (up to 40 px around). Irrelevant for one or two CTAs; beyond that, move the shadow to a pseudo-element and animate its opacity (see the FAQ).
Accessibility
- prefers-reduced-motion not handled: the pulse runs forever for everyone. Add
@media (prefers-reduced-motion: reduce) { .btn-glow { animation: none; box-shadow: 0 0 20px rgba(99,102,241,.4) } }— the halo stays, static, and the emphasis is preserved. - Contrast: white text on
#6366f1= 4.47:1, just under the 4.5:1 AA threshold for 16 px text.#4f46e5gives 6.3:1 — change the background and both shadow colors together so the halo stays the button's own color. - The halo carries a meaning (“this is the action to take”) that exists only visually: a screen reader hears an ordinary button. If that hierarchy matters (next step of a flow), say it in the label (“Step 3: connect payments”) or through
aria-describedby. - The code sets
border: nonewithout touchingoutline: the native focus ring stays. On a dark background it can blend with the pulsing halo — declare.btn-glow:focus-visible { outline: 2px solid #fff; outline-offset: 3px }to tell them apart. - Pulse at 0.5 Hz, far below the three-flashes-per-second threshold (WCAG 2.3.1). Keep it to a single element per screen: several pulsing buttons cancel each other out and add visual noise. Touch target ≈ 52 px tall, above the 44 px minimum.
Browser compatibility
Requires a CSS @keyframes animation on box-shadow — supported everywhere since 2012. No JavaScript, zero dependencies.
Without CSS animation support, the button renders with its indigo background and no halo at rest; the 50 px hover halo remains, since it is a plain box-shadow.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<button class="demo-btn btn-glow">Glow Pulse</button>
The full HTML + CSS + JS of Glow Pulse, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| animation-duration (CSS — .btn-glow) | 2s | Length of a full cycle (swell then fade). 1s = alert; 3–4s = slow breathing. |
| Radii 20px / 40px (CSS — @keyframes glowPulse) | 20px → 40px | Halo spread at rest and at peak. Keep a ratio close to 2 so the pulse is visible; beyond 60 px the halo becomes a colored background. |
| Opacities .4 / .8 (CSS — @keyframes glowPulse) | .4 → .8 | Min and max intensity. Go down to .2 → .5 on a light background, where a colored halo shows less. |
Halo color (CSS — rgba(99,102,241,…) ×2 + #6366f1 on hover) |
indigo #6366f1 | Three occurrences to change along with background. The halo must keep the button's color to read as emitted light. |
| Hover box-shadow (CSS — .btn-glow:hover) | 0 0 50px #6366f1 | Frozen hover state. 30px for a plain stop of the pulse; add transition: box-shadow .2s on .btn-glow to soften the jump. |
| animation-timing-function (CSS — .btn-glow) | ease-in-out | linear gives a mechanical pulse; cubic-bezier(.4, 0, .6, 1) lengthens the time spent at the extremes. |
| animation-delay (CSS — .btn-glow) | 0s | Offset several buttons (-1s, -.5s) so they don't pulse in phase — or, better, let only one pulse. |
FAQ
box-shadow is not GPU-composited. One or two buttons: imperceptible. Beyond that, move the shadow to an ::after positioned under the button, carrying the maximum box-shadow, and animate its opacity from .4 to 1 — opacity is composited, the repaint goes away.animation: none cuts the loop dead and sets a fixed 50 px shadow with no transition; when the cursor leaves, the animation restarts at its 0% (20 px). Add transition: box-shadow .25s on .btn-glow to smooth the entry; the restart at 20 px will remain — that's how CSS animation works, not a bug..is-done) that declares animation: none and a light shadow, and set it on click or on the server's response — three lines of JS. A halo pulsing on an already completed step is the most common misuse of this effect.