Buttons✨ Premium

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.

CSSAnimationGlow

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

Next step of an onboarding flow — Glow Pulse example 1

① Next step of an onboarding flow

WhenSaaS setup checklist: two steps checked, the third one waits for the user.
WhyThe pulse points at the only useful action on the screen without a banner or an arrow; at 2 s it stays slow. A single button pulses, which is what keeps it readable.
Settings2.5 s cycle, halo 16 → 32 px, halo color = the app's primary color.
Join a live stream — webinar or product launch — Glow Pulse example 2

② Join a live stream — webinar or product launch

WhenCard or hero announcing an event in progress, with a “Live” badge.
WhyThe breathing glow echoes a recording indicator; the halo freezing on hover confirms you are on the entry button.
Settings1.5 s cycle (more pressing), opacities 0.5 → 0.9, red #ef4444 to match the “live” color code.
Empty state of a dashboard — Glow Pulse example 3

③ Empty state of a dashboard

WhenFirst screen of a new account: no project yet, one sentence, one create button.
WhyThe screen is empty by definition: the pulse gives it a starting point. The wide halo works because it has no neighbor.
Settings3 s cycle for a calm rhythm, halo 24 → 56 px, hover frozen at 64 px.

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. #4f46e5 gives 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: none without touching outline: 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.

Chrome 26+✓ Full
Firefox 16+✓ Full
Safari 9+✓ Full
Edge 12+✓ Full
Mobile iOS✓ Full (:hover sticks after a tap)
Android Chrome✓ Full

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):

index.html — structure
<button class="demo-btn btn-glow">Glow Pulse</button>
Unlock the full code — $9.90/mo incl. tax

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 / propertyDefaultEffect
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

Every frame repaints the button and its blur area (up to 40 px around): 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.
Two reasons in the code: on hover, 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.
Add a state class (.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.