Ripple click
A circle expands from the exact click point and fades, the Material-style press feedback.
CSSJavaScriptBeginner
<button class="btn" id="b">Click anywhere on me</button>
.btn{position:relative;overflow:hidden;font:inherit;font-weight:700;font-size:1.1rem;color:#270100;background:#f08a5d;border:0;border-radius:12px;padding:18px 40px;cursor:pointer}
.ripple{position:absolute;border-radius:50%;background:rgba(39,1,0,.28);transform:scale(0);animation:ripple .7s cubic-bezier(.2,.8,.2,1);pointer-events:none}
@keyframes ripple{to{transform:scale(1);opacity:0}}
document.getElementById('b').addEventListener('click', e => {
const btn = e.currentTarget;
const r = btn.getBoundingClientRect();
const size = Math.max(r.width, r.height) * 2.2;
const dot = document.createElement('span');
dot.className = 'ripple';
dot.style.width = dot.style.height = size + 'px';
dot.style.left = (e.clientX - r.left - size / 2) + 'px';
dot.style.top = (e.clientY - r.top - size / 2) + 'px';
btn.append(dot);
dot.addEventListener('animationend', () => dot.remove());
});
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Ripple click</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
.btn{position:relative;overflow:hidden;font:inherit;font-weight:700;font-size:1.1rem;color:#270100;background:#f08a5d;border:0;border-radius:12px;padding:18px 40px;cursor:pointer}
.ripple{position:absolute;border-radius:50%;background:rgba(39,1,0,.28);transform:scale(0);animation:ripple .7s cubic-bezier(.2,.8,.2,1);pointer-events:none}
@keyframes ripple{to{transform:scale(1);opacity:0}}
</style></head>
<body><button class="btn" id="b">Click anywhere on me</button>
<script>
document.getElementById('b').addEventListener('click', e => {
const btn = e.currentTarget;
const r = btn.getBoundingClientRect();
const size = Math.max(r.width, r.height) * 2.2;
const dot = document.createElement('span');
dot.className = 'ripple';
dot.style.width = dot.style.height = size + 'px';
dot.style.left = (e.clientX - r.left - size / 2) + 'px';
dot.style.top = (e.clientY - r.top - size / 2) + 'px';
btn.append(dot);
dot.addEventListener('animationend', () => dot.remove());
});
</script>
</body></html>
Good for
Buttons, cards and list rows that need clear tap feedback.
Watch out for
Clip with overflow hidden on the button and remove each ripple after it ends.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.