Ring the press, not the fill
A ring animation shows on a fast tap, and stays apart from hover.
Sam Selikoff@samselikoffNoticed this ring/outline animation on press this morning. First time seeing this treatment, I like it. Much more subtle than the typical background "ripple" (which always felt gratuitous to me). The animation (rather than transition) means it works even for a fast tap. And using the border rather than the background lets you differentiate completing the press from both hover and mouseDown. Nice!
1 · Tap fast
Tap each button quickly. Hover only tints the background.
:active transitionBad
:active { box-shadow }Presses0Ring animationGood
el.animate(ring)Presses0button.addEventListener("pointerdown", ring);
button.addEventListener("keydown", (e) => {
if (!e.repeat && (e.key === "Enter" || e.key === " ")) ring();
});
const ring = () => button.querySelector(".ring").animate(
[{ boxShadow: "0 0 0 0 var(--ring-color)", opacity: 1 },
{ boxShadow: "0 0 0 6px var(--ring-color)", opacity: 0 }],
{ duration: 350, easing: "cubic-bezier(0.23, 1, 0.32, 1)" });
2 · Ripple or ring
Press each button. The ripple fills the whole surface.
Background rippleBad
ripple from pointerPresses0Ring outlineGood
ring on the edgePresses0Why it matters, pros and cons
Why it matters
An animation runs to the end even on a 60ms tap, so every press gets seen.
Pros
- Subtler than a ripple, which fills the surface and draws too much.
- The edge ring tells a completed press apart from hover and mousedown.
- Starts on pointerdown and on Enter or Space, so mouse, touch and keys agree.
Cons
- Needs a restart on each press, or a rapid second tap shows nothing new.
- A ring on a button that sits flush with others can get clipped.
- Under reduced motion it has to become a fade, not a moving ring.