0 Pure CSS7 with JSPublished 2026-10-05Free · no sign-up
Seven buttons that animate when you use them: a like button with a burst, a send button whose paper plane flies off, a Follow toggle, an add to cart dot that flies into the cart, confetti on completing a task, a hold-to-delete button and a subscribe button with a ringing bell.
Pressing like fills the heart, pops it slightly larger and sends out a ring of small dots, all from CSS on the pressed state. The button is a toggle with aria-pressed and its count updates in the same text, so the state is clear without the burst.
var b = document.querySelector('.abt1'), c = b.querySelector('.c');
b.onclick = function () { var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); c.textContent = +c.textContent + (on ? 1 : -1); };
02. Send Button with Paper Plane
AnimatedFeedback
On click the paper plane flies off to the top right, the label changes to Sent, and a moment later everything returns. The flight is a keyframe on the icon only, the label change is real text, and a live region announces Message sent.
var b = document.querySelector('.abt2 button'), l = b.querySelector('.l'), live = document.querySelector('.abt2 .sr');
b.onclick = function () { if (b.classList.contains('go')) return; b.classList.add('go'); l.textContent = 'Sent'; live.textContent = 'Message sent'; setTimeout(function () { b.classList.remove('go'); l.textContent = 'Send'; }, 1800); };
03. Follow to Following Swap
AnimatedToggle
The Follow label slides up and away as Following with a tick slides in, and turns quiet grey so the pressed state does not shout. Both labels sit in the same grid cell, so the button is always as wide as the longer one and nothing jumps when they swap.
var b = document.querySelector('.abt3');
b.onclick = function () { var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); };
04. Add to Cart Fly-In
AnimatedEcommerce
A dot flies from the Add to cart button into the cart icon along a curve, then the cart count bumps. The curve comes from animating the x and y offsets on two nested elements with different easings, a classic trick for arcs without paths.
var w = document.querySelector('.abt4'), n = w.querySelector('.n'), cart = w.querySelector('.cart'), k = 0;
w.querySelector('button').onclick = function () {
var fx = document.createElement('span'); fx.className = 'fx'; fx.innerHTML = '<i></i>'; fx.setAttribute('aria-hidden', 'true'); w.appendChild(fx);
setTimeout(function () { fx.remove(); k++; n.textContent = k; cart.setAttribute('aria-label', 'Cart, ' + k + (k === 1 ? ' item' : ' items')); n.classList.remove('bump'); void n.offsetWidth; n.classList.add('bump'); }, matchMedia('(prefers-reduced-motion: reduce)').matches ? 0 : 600);
};
05. Confetti Celebration Button
AnimatedCelebration
Completing a task sprays a short burst of confetti from the button. The pieces are a dozen small spans given random directions through custom properties, removed after one second, and skipped entirely under reduced motion.
View code
<div class="abt5"><button>Mark as done</button></div>
var w = document.querySelector('.abt5'), b = w.querySelector('button'), cols = ['#f43f5e', '#f59e0b', '#22c55e', '#3b82f6', '#a855f7'];
b.onclick = function () {
var done = b.classList.toggle('done'); b.textContent = done ? 'Done ✓' : 'Mark as done';
if (!done || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
for (var i = 0; i < 14; i++) { var s = document.createElement('span'), a = Math.random() * Math.PI * 2, d = 60 + Math.random() * 60; s.className = 'cf'; s.setAttribute('aria-hidden', 'true');
s.style.cssText = '--c:' + cols[i % 5] + ';--x:' + Math.cos(a) * d + 'px;--y:' + Math.sin(a) * d + 'px;--r:' + (Math.random() * 720 - 360) + 'deg';
w.appendChild(s); setTimeout(s.remove.bind(s), 1000); }
};
06. Press and Hold to Confirm
AnimatedDestructive
A destructive button that only fires after being held for a second and a half, with a fill showing progress. It works with the pointer and with Space or Enter held on the keyboard, and releasing early cancels. The instruction is in the button's description.
View code
<div class="abt6"><button aria-describedby="abt6h"><span class="f" aria-hidden="true"></span><span class="l">Hold to delete</span></button><p id="abt6h">Press and hold for 1.5 seconds.</p></div>
var b = document.querySelector('.abt6 button'), l = b.querySelector('.l'), t;
function start(e) { if (e.type === 'keydown' && (e.repeat || (e.key !== ' ' && e.key !== 'Enter'))) return; if (e.type === 'keydown') e.preventDefault(); if (b.classList.contains('ok')) return; b.classList.add('hold'); t = setTimeout(function () { b.classList.remove('hold'); b.classList.add('ok'); l.textContent = 'Deleted'; }, 1500); }
function stop() { clearTimeout(t); b.classList.remove('hold'); }
b.addEventListener('pointerdown', start); b.addEventListener('keydown', start);
['pointerup', 'pointerleave', 'keyup', 'blur'].forEach(function (ev) { b.addEventListener(ev, stop); });
b.addEventListener('click', function (e) { e.preventDefault(); });
07. Subscribe with Ringing Bell
AnimatedToggle
Subscribing turns the button solid and gives its bell a short ring, a satisfying confirmation borrowed from video platforms. The ring plays once when the pressed state becomes true and never loops.
var b = document.querySelector('.abt7'), s = b.querySelector('span');
b.onclick = function () { var on = b.getAttribute('aria-pressed') !== 'true'; b.setAttribute('aria-pressed', on); s.textContent = on ? 'Subscribed' : 'Subscribe'; };
About these CSS Animated Buttons
Animate the result, not the click
These animations confirm that something happened: liked, sent, added, deleted. Run them after the state changes and pair them with a visible change in text or icon fill that persists.
Toggles report their state
Like, follow and subscribe are toggles. aria-pressed tells assistive technology whether they are on, and CSS can style the pressed state directly from the attribute.
Hold-to-confirm needs a keyboard path
Long-press buttons must also work by holding Space or Enter, and say how long to hold. Releasing early should cancel without side effects.
Keep particles disposable
Confetti and bursts are short-lived spans: create them, let them animate, then remove them. Skip them entirely under prefers-reduced-motion, since the state change is what matters.
Frequently asked questions
Do these buttons do anything real?
They show the interaction only. Wire them to your own requests and keep the animation on success.
Can I use these with React or Vue?
Yes. Toggle the same attributes and classes from component state.
Is confetti too much?
For a milestone, no. For every click, yes; keep celebrations for genuine achievements.