There's a version of every website where every button bounces, every card flips, every element fades in on scroll, and the whole thing feels like a theme park. You've seen these sites. You probably didn't stay long.
Then there's the opposite. Everything is instant, nothing moves, and clicking a button feels like poking a dead thing. These work, but they feel cheap.
The difference between good micro-interactions and bad ones isn't how many you have. It's whether each one answers a question the user is asking without realizing it.
Every time a user does something on your interface, there's a tiny moment of uncertainty. Did that click register? Is something loading? Did that item get added? Where did that panel go?
Good micro-interactions answer these questions before the user has to think about them. A button that subtly depresses on click says "yes, I heard you." A skeleton screen says "content is coming." A list item that slides out before disappearing says "this thing left, it didn't just vanish."
The pattern is always the same: something happened, and the animation confirms it. If an animation isn't confirming an action or clarifying a state change, ask yourself why it's there.
Button feedback. A slight scale-down on press
(active:scale-95) is one of the highest-value animations you
can add. It costs nothing performance-wise, takes one line of CSS, and
makes the whole interface feel more responsive. Without it, tapping a
button on mobile feels uncertain. With it, every tap feels acknowledged.
State transitions on toggles. When a toggle changes state, a smooth transition between positions tells the user what changed. An instant swap between on and off feels broken, especially when the toggle controls something the user can't immediately verify, like a setting.
Content entering and leaving. When a modal opens, it should transition in. A subtle scale-up, a fade, a slide. When it closes, it should leave the same way. The animation tells the user where this thing came from and where it went. It maintains spatial consistency.
Loading indicators. A spinner or progress bar during a network request is the most basic micro-interaction, and skipping it is one of the most common mistakes. The user clicked something and nothing changed visually. They'll click again, and again, and now you have three duplicate requests.
Hover states on interactive elements. If something is clickable, it should react to hover. A background color shift, a subtle lift, an underline appearing. Doesn't matter which, as long as there's a signal. This is especially important for cards and list items where the clickable boundary isn't obvious.
Scroll-triggered entrance animations. Every element fading in as you scroll. This was novel in 2015. Now it just makes your page feel slow because the user has to wait for content they've already scrolled to. The content is there. Show it.
Fake loading states. If the data came with the page (SSG, SSR, or static text), don't fake a loading animation with a staggered fade-in. You're manufacturing a delay that doesn't exist.
Hover on non-interactive elements. If a paragraph or heading does a color shift on hover, you're signaling it's interactive when it isn't. Every hover effect is a promise that something will happen on click.
Complex page transitions. Full-page slide transitions between routes. Unless spatial navigation is core to the experience (like a presentation tool), page transitions add latency to every navigation. The user wanted to go somewhere. Let them.
Parallax everything. A background layer moving at a different speed than the foreground. It's disorienting for people with motion sensitivities, expensive to render smoothly, and almost never serves the content.
Most animations that feel "off" aren't too fast or too slow. They have the wrong easing curve.
linear almost never looks right for UI. Things in the real
world don't move at constant speed.
ease-in (slow start, fast end) feels like something falling
away. Good for exits.
ease-out (fast start, slow end) feels like something arriving
and settling. Good for entrances.
ease-in-out is what people reach for by default, but it often
feels mushy. The slow start adds perceived latency.
For most UI transitions, ease-out with 150ms to 250ms is a
good starting point. Short enough to feel instant. Long enough to be
perceived.
Respect prefers-reduced-motion. Some people experience motion
sickness from animations. Some just find them distracting. Wrapping your
animations in a media query is trivial:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
This doesn't remove state changes. The button still looks pressed. The toggle still moves. Transitions just happen instantly. The information is preserved, the motion is removed.
Before adding any animation, ask: "If I remove this, does the user lose information?" If yes (a state change becomes invisible, a spatial relationship breaks, feedback disappears) keep it. If no, it's decoration. Might still be worth keeping for polish, but be honest about what it is.
The best interfaces have animations you don't notice. You just feel like everything works.