Like button burst
Tapping the heart pops it with a spring and releases a small ring of particles.
CSSJavaScriptIntermediate
<button class="like" id="b" aria-pressed="false" aria-label="Like">
<svg viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
.like{position:relative;width:84px;height:84px;border-radius:50%;border:2px solid #4a2e2a;background:#1f0f0e;cursor:pointer;display:grid;place-items:center;color:#a6938f;transition:color .2s,border-color .2s}
.like svg{width:38px;height:38px;fill:currentColor;transition:transform .2s}
.like:active svg{transform:scale(.8)}
.like[aria-pressed=true]{color:#ff4d6d;border-color:#ff4d6d}
.like.pop svg{animation:pop .55s cubic-bezier(.34,1.56,.64,1)}
@keyframes pop{0%{transform:scale(.5)}60%{transform:scale(1.3)}100%{transform:scale(1)}}
.p{position:absolute;left:50%;top:50%;width:7px;height:7px;margin:-3.5px;border-radius:50%;background:#ff4d6d;pointer-events:none;
animation:fly .6s cubic-bezier(.2,.8,.2,1) forwards}
@keyframes fly{to{transform:translate(var(--dx),var(--dy)) scale(0);opacity:0}}
const b = document.getElementById('b');
b.onclick = () => {
const on = b.getAttribute('aria-pressed') !== 'true';
b.setAttribute('aria-pressed', on);
b.classList.remove('pop'); void b.offsetWidth;
if (!on) return;
b.classList.add('pop');
for (let i = 0; i < 10; i++) { // ring of particles
const p = document.createElement('i');
const a = (i / 10) * Math.PI * 2, r = 46 + Math.random() * 14;
p.className = 'p';
p.style.setProperty('--dx', Math.cos(a) * r + 'px');
p.style.setProperty('--dy', Math.sin(a) * r + 'px');
b.append(p);
p.addEventListener('animationend', () => p.remove());
}
};
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Like burst</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}
.like{position:relative;width:84px;height:84px;border-radius:50%;border:2px solid #4a2e2a;background:#1f0f0e;cursor:pointer;display:grid;place-items:center;color:#a6938f;transition:color .2s,border-color .2s}
.like svg{width:38px;height:38px;fill:currentColor;transition:transform .2s}
.like:active svg{transform:scale(.8)}
.like[aria-pressed=true]{color:#ff4d6d;border-color:#ff4d6d}
.like.pop svg{animation:pop .55s cubic-bezier(.34,1.56,.64,1)}
@keyframes pop{0%{transform:scale(.5)}60%{transform:scale(1.3)}100%{transform:scale(1)}}
.p{position:absolute;left:50%;top:50%;width:7px;height:7px;margin:-3.5px;border-radius:50%;background:#ff4d6d;pointer-events:none;
animation:fly .6s cubic-bezier(.2,.8,.2,1) forwards}
@keyframes fly{to{transform:translate(var(--dx),var(--dy)) scale(0);opacity:0}}
</style></head>
<body>
<button class="like" id="b" aria-pressed="false" aria-label="Like">
<svg viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
</button>
<script>
const b = document.getElementById('b');
b.onclick = () => {
const on = b.getAttribute('aria-pressed') !== 'true';
b.setAttribute('aria-pressed', on);
b.classList.remove('pop'); void b.offsetWidth;
if (!on) return;
b.classList.add('pop');
for (let i = 0; i < 10; i++) { // ring of particles
const p = document.createElement('i');
const a = (i / 10) * Math.PI * 2, r = 46 + Math.random() * 14;
p.className = 'p';
p.style.setProperty('--dx', Math.cos(a) * r + 'px');
p.style.setProperty('--dy', Math.sin(a) * r + 'px');
b.append(p);
p.addEventListener('animationend', () => p.remove());
}
};
</script>
</body></html>
Good for
Likes, favourites and reactions. A little delight at the exact moment of the action.
Watch out for
Toggle aria-pressed so the state is announced, and remove burst particles after the animation ends.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.