Reaction Burst Button
A like button that springs its icon, throws a radial burst of pooled particles and rolls its count over one digit at a time.
About This Component
A reaction button that answers the press. The icon fills and springs back on an elastic ease, a ring pulses out from behind it, eleven particles fly out on their own angles and fade, and the count rolls over instead of swapping. Press it again and the whole thing reverses on a quick flat shrink, so undoing never reads as another celebration.
Read the full effect overview
The particles are pooled spans, not a canvas: eleven elements are created once, appended for the burst, removed when their tween ends and handed back to a pool for the next press. Ships as a row of three reactions, and the demo can drop it to a single button to show the same component solo. One HTML file, one stylesheet, one script, GSAP core only, and three shipped looks switched by a data-variant attribute.
What's Included
- Icon springs back on an elastic ease, with a ring pulse behind it
- Radial burst of eleven pooled DOM particles, no canvas and no library
- Particles are removed from the DOM on completion and reused on the next press
- Count rolls one digit up on press and back down on un-press
- Un-press is a short flat shrink, not a second celebration
- Per-reaction accent colour read from a custom property on the button
- Reduced motion skips the particles entirely and flips the state instantly
- Real buttons with aria-pressed and a live count in the accessible name
- window.reactionBurst.toggle / count / reset for your own code
- Three shipped variants: vault, brutal, corporate
Perfect For
- Post and comment reactions in social feeds and communities
- Article likes, claps and saves on editorial sites
- Upvotes and favourites in dashboards and internal tools
- Any counter currently incrementing with no acknowledgement at all
How It Works
Pooled particles, not a canvas
A burst asks a pool array for eleven spans, creating one only when the pool is empty. Each is appended to a zero-size origin point at the centre of the icon, given an angle spread evenly around the circle with a small random jitter and a random distance, then tweened out on x and y while its scale and opacity fall away. On completion the span is removed from the DOM and pushed back to the pool, so a hundred presses still allocate eleven elements.
The spring is the ease, not a physics loop
The press sets the icon to a scale of 0.55 and tweens it to 1 on elastic.out(1, 0.42). That single ease carries the overshoot and the settle, which is why the whole gesture costs one tween. Nothing in the CSS sets a transform on the icon, the ring or a particle, so every scale, x and y starts from a real zero.
Rolling one digit
The count is a clipped box with two stacked spans. The incoming value is written into the hidden one, placed a full height below (or above, when the count goes down) with yPercent, and the two are tweened past each other. When the tween lands the visible span takes the new text, both spans are reset to their resting offsets and the hidden one is emptied, so the next roll starts from a clean stack.
Colours come from the variant
Each button carries its own --rx-accent, mapped in one rule per reaction to the variant's heart, star and clap colours. The script reads that property with getComputedStyle on the button at the moment a burst fires, so particles and the ring are always the right colour and a fourth reaction needs one CSS rule and no JavaScript. Under prefers-reduced-motion the burst and the ring are skipped and the state flips with no motion at all.