Guides · · Maxwell Prehoda
13 Button Click Animation Examples That Make Every Press Feel Real
A good click animation answers one question within a frame or two: did that register? The press itself should be nearly instant (GitHub's buttons use no transition at all on press, Josh W. Comeau's 3D button uses 34ms), and the personality goes into the release and into what happens next: a ripple, a spinner that becomes a check, a burst, or confetti for the rare big moment.
Below are 13 examples, from design systems and libraries to tutorials and Codrops experiments, with the timings from their source code or docs, what each is best for and the catch. Most have a live pure-CSS demo from our free directory that you can press right here and copy. Hover effects are a different job, covered in our button hover effect examples.
1. Emil Kowalski's 0.97 press: Best for making any primary button feel pressable
Platform: Pure CSS technique, one :active rule
The simplest click animation worth shipping comes from Emil Kowalski's 7 practical animation tips: scale the button to 0.97 on :active. The button shrinks by 3% while the pointer is down, which reads as a physical press without anyone noticing an animation.
The same post warns against the mirror mistake, animating an element in from scale(0); start from 0.9 or higher so it never appears out of nothing. It also suggests keeping UI animations under 300ms. Our Pressed Button Effect below uses exactly scale(0.97), plus a slight dip and a drop shadow that collapses into an inner shadow: 0.08s on the way down, a springy 0.4s on the way back up.
Watch out for: Scale shrinks the label too, and 3% of a full-width mobile button is a lot of pixels. On wide buttons, use a smaller scale or a 1px to 2px translate instead.
2. GitHub Primer: no animation on press: Best for dense product UI where people click all day
Platform: Primer React (GitHub's design system), MIT
GitHub's buttons do not animate the press at all, and you can read the decision in the source. In Primer React's ButtonBase styles, colour, fill, background and border transition over 80ms on cubic-bezier(0.65, 0, 0.35, 1), but the :active rule sets transition: none, so the pressed colour appears on the very frame the pointer goes down.
That is the right call for buttons people press hundreds of times a day, like Merge, Comment and Approve. Any delay on press feels like lag, and an instant colour change feels like the click landed. Use it as your default, then add motion only where the moment deserves it.
Watch out for: Instant feedback only works if the pressed colour is clearly different from the hover colour. If the two are close, a quick click shows nothing at all. Check rest, hover and pressed side by side.
4. Material Web's ripple: Best for Material design systems and Android-style web apps
Platform: Material Web (Lit web components), Apache 2.0
The ripple is Material Design's signature press. In Material Web's source, the md-ripple element starts a circle 20% of the control's larger side at the point you pressed, then grows it over 450ms while drifting it to the centre, so it covers the whole control. The pressed layer fades in over 105ms to 12% opacity and fades out over 375ms, and the ripple stays at least 225ms even on a very quick click, so it is always seen. With no pointer position to start from, it starts in the centre.
Touch gets special care: md-ripple waits 150ms after a touch begins before it shows the press, so a finger that is starting a scroll does not light up every button it crosses.
Our Ripple Click Button below is a pure-CSS take. Because CSS cannot read where you clicked, the circle always grows from the centre, over 0.6s while the button is held, and fades as you let go.
Watch out for: Outside a Material look, a ripple on small icon buttons or busy surfaces reads as noise. Material pairs it with a hover state layer and a focus ring; it is not meant to carry the feedback alone.
6. Motion's whileTap: Best for React apps that want spring physics on press
Platform: React + Motion (formerly Framer Motion), MIT
Motion makes press feedback one prop: whileTap={{ scale: 0.9 }} on a motion.button animates while the pointer is down and springs back on release. The gesture docs note that a tap only counts if it ends on the same element (otherwise onTapCancel fires), and that pressing Enter on a focused element triggers whileTap too, so keyboard users get the same feedback.
Springs earn their keep on the release: they settle from wherever the button happens to be, so a tap cut short halfway still returns naturally. Our Spring Press Button below gets a similar feel in pure CSS: it squashes to scale(1.1, 0.8) in 0.15s on press and springs back over 0.9s through a linear() easing sampled from a spring.
Watch out for: Motion ignores the reduced-motion setting by default. Set reducedMotion="user" on MotionConfig to drop transform animations for people who ask. Also, 0.9 is a big squeeze for everyday buttons; 0.95 to 0.97 is calmer.
7. Ladda by Hakim El Hattab: Best for form submit buttons that wait on a server
Platform: JavaScript + CSS, MIT, npm install ladda
Hakim El Hattab's Ladda put the loading indicator inside the button that started the action, so the feedback appears exactly where the person is looking. It has a dozen ways for the spinner to arrive: expand left, right, up or down, contract, contract overlay, zoom in, zoom out, and slide left, right, up or down. The expand-right and contract styles can also show a built-in progress bar.
The pattern it popularised is now standard: on click, disable the button, swap the label for a spinner, then swap the spinner for the result. Our Submit, Loading, Success Button below plays that whole sequence in pure CSS: the pill shrinks into a spinning ring, then grows back green and draws a check.
Watch out for: The Ladda repository has not been updated since December 2021. And our demo runs on a timer for show; in a real form, drive each state from the actual request and announce the result in an aria-live region.
10. mo.js Burst: Best for small celebratory bursts on icon buttons
Platform: JavaScript motion library, MIT
mo.js, the motion graphics library by Oleg Solomko (legomushroom), has a Burst module built for click effects: it arranges a ring of particles around a point and sends them outward. count sets how many particles there are, radius sets how far the ring spreads, and children passes shape, fill, duration and delay to every particle.
Bursts work because they are short and centred on the thing you touched. Fire one at the click point and the press feels like it released a little energy. Our Clap Button below is a pure-CSS take: each press tips the hand, and each release sends a +1 floating up with a burst of short lines.
Watch out for: mo.js is a whole library for a few hundred milliseconds of effect. If a burst is all you need, a few pseudo-elements or a tiny canvas will do it for far less code.
11. canvas-confetti by Kiril Vatev: Best for rare big moments like Publish, Upgrade or Launch
Platform: JavaScript, ISC licence, npm canvas-confetti
canvas-confetti is the go-to library for a celebration on click. A single confetti() call fires a burst from the middle of the screen, and the options shape it: particleCount (50 by default), spread (45 degrees by default), origin, angle, gravity, colours and shapes. Set origin to the button's position and the confetti comes out of the thing that was pressed.
It also has the most useful accessibility option of any library here: disableForReducedMotion skips the confetti entirely for people who ask their device for less motion.
Watch out for: disableForReducedMotion defaults to false, so pass true every time. And confetti on every click turns a celebration into noise; save it for the end of something that took effort. For quieter ways to say done, see our success animation examples.
How to choose a button click animation
Three things decide it: how often the button is pressed, what happens after the press, and who is pressing it.
Match it to how often it is pressed
Buttons pressed all day get instant feedback and nothing else: an instant colour change like Primer, or a 0.97 scale like Emil Kowalski's tip. Buttons pressed a few times a session can carry a 3D push or a ripple. Bursts and confetti are for buttons pressed once, at the end of something. The rarer the press, the more the animation can say.
Match it to what happens next
If the result is instant, the press animation is the whole story. If the button waits on a server, turn it into its own loading state, the way Ladda and the Submit, Loading, Success Button do, and disable it so it cannot be pressed twice. If the action cannot be undone, slow it down on purpose with a hold to confirm. For likes and other toggles, see our like button animation examples.
Match it to the input and the motion settings
Keep the press itself under about 100ms, then spend 200ms to 600ms on the release. Make sure keyboard presses get feedback too: Motion's whileTap fires on Enter, and an instant :active colour covers the Space key for free. Under prefers-reduced-motion, keep the colour change and the result state, and drop the scaling, tilting, particles and confetti.
Make your buttons answer back today
Give every button an instant pressed colour and a small scale on :active, then pick one moment in your product that deserves more: a loading-to-success morph on the main form, a hold on the delete button, or a burst on the one action people celebrate. Copy the CSS from our Buttons directory and set the colour with the custom property.
If you want a press nobody else has, describe it to the creator. It asks up to four quick questions while it works and hands back production-ready CSS keyframes.
Make a click animation for a Save button in a web app. On press, scale to 0.97 in 60ms with ease-out; on release, spring back over 300ms with a slight overshoot. After the click, swap the label for a small spinner, then for a check mark and the word Saved, then reset after 1.5s. Animate only transform and opacity, and keep keyboard focus on the button throughout. Under prefers-reduced-motion, skip the scale and spinner motion and just swap the labels.
Common questions
How do you make a button click animation in CSS?
Add a transition to the button, then change it in :active, for example transform: scale(0.97). Make the press fast (under about 100ms) and the release slower. For states that last after the click, such as loading or done, toggle a class with JavaScript, or use a hidden checkbox the way the demos in our Buttons directory do.
What is the ripple effect on a button?
It is Material Design's press feedback: a translucent circle that grows from the point you pressed until it covers the button, then fades. Material Web grows it over 450ms and keeps it at least 225ms so quick clicks still show it. CSS alone can only grow it from the centre; starting it at the click point needs JavaScript.
How long should a button click animation last?
The press should feel instant: GitHub's Primer uses no transition at all on press, Josh W. Comeau's 3D button uses 34ms. The release and any follow-up state can take 200ms to 600ms. Anything slower than that on press reads as lag.
Can you animate a button click without JavaScript?
Yes, for anything that happens while the button is held, since :active covers press scale, ripples, tilts and hold-to-confirm. A state that persists after the click needs either a hidden checkbox or JavaScript, and in a real form it should follow the actual request, not a timer.
Should click animations respect prefers-reduced-motion?
Yes. Keep the colour change and the final state, since they carry the meaning, and remove scaling, tilting, particles and confetti. canvas-confetti has a disableForReducedMotion option, and Motion's MotionConfig takes reducedMotion="user"; neither is on by default.
Sources
- Emil Kowalski: 7 practical animation tips
- Primer React ButtonBase styles
- Josh W. Comeau: Building a Magical 3D Button
- Material Web: Ripple
- Material Web ripple source
- React Aria: Ripple Button example
- Motion: React gestures
- Motion: MotionConfig reducedMotion
- Ladda by Hakim El Hattab
- Codrops Progress Button Styles source
- Codrops Particle Effects for Buttons source
- mo.js Burst tutorial
- canvas-confetti on GitHub