Overview
A pulsing focus ring breathes its outline-offset outward and back while focus is held. The element box never shifts; only the outline radius changes. Three production contexts ship: a primary button CTA, a tight inline-link ring, and a wider form-field ring that clears the input border.
When to use it
Reach for pulsing focus on hero CTAs and marketing surfaces where the focused element should pull attention — a loud signal that “this is where keyboard input lands next.” Skip it for dense forms or list-row focus — every focused row pulsing simultaneously is exhausting. Always pair with :focus-visible so mouse clicks do not trigger the pulse.
How it works
The element gets a static outline on :focus-visible — a fixed color and width so the visible “border” never changes. A @keyframes pulse rule then animates only outline-offset between two values (e.g. 2px to 6px), which expands the gap between the outline and the element edge without triggering layout. The outline lives outside the box model entirely, so neighbors do not shift even though the ring breathes. Pair with animation-direction: alternate for the in-and-out breathing, or chain explicit keyframes for an asymmetric ease.
Production gotchas
Using box-shadow instead of outline looks similar in light mode but disappears entirely in forced-colors mode — stick with outline for the actual ring and keep any box-shadow as a decorative halo only. The pulse animation must stop when focus leaves, but CSS animations do not auto-cancel mid-cycle; rely on the :focus-visible selector dropping to halt the rule, and write the keyframes so the resting state at 100% matches the static outline. Wide pulses on tightly-packed inputs can overlap adjacent fields visually — cap the maximum offset at the gap between fields.
Accessibility
The static focused state must already meet WCAG 2.4.13 on its own — the pulse is decoration, not the indicator. Under prefers-reduced-motion: reduce set animation: none on the pulse so the static outline remains and the breathing motion stops entirely. Avoid pulses faster than ~3Hz to stay under the photosensitivity threshold; aim for a 1.4–2 second full breath cycle.
References
Implementation depth
A pulsing focus ring should communicate location without moving the element box. Animate outline-offset or a shadow layer around the control, then keep the component dimensions fixed.
Focus is an accessibility signal, so never make it subtle just to preserve aesthetics. Reduced motion can freeze the ring at its widest clear state while still giving keyboard users a strong target.