From the click coordinates, create a circle large enough to cover the whole button (`Math.max(width, height) * 2` in diameter), centered on the click point. Set the button to `overflow: hidden` so the circle gets clipped at its edges.
Animate from `transform: scale(0)` to `scale(1)` while fading `opacity` out, via `@keyframes`, and remove the element on `animationend` — cleaning it up matters so the DOM doesn't accumulate leftover ripples.
On a dark button background, a semi-transparent white circle (`rgba(255,255,255,.5)`) reads well; on light, use a dark one. Add a fresh circle on every click so rapid taps overlap naturally instead of resetting.
When to use
Use it wherever a tap needs an immediate visual acknowledgement — primary buttons, list-item taps. It can be overkill on buttons that already have a strong hover state.