How fast UI animations should be
Sep 18, 2026 · Motion · 3 min read
Good motion is mostly about restraint. An animation that is lovely the first time is irritating by the twentieth, and some interactions happen hundreds of times a day. The single most useful rule I know is this: the more often something happens, the less it should animate.
Give buttons a little when pressed
A button that does nothing visible between press and release feels dead, especially when the action takes a moment. A small scale on :active tells the hand that the press landed.
.button {
transition: scale 150ms ease-out;
}
.button:active {
scale: 0.97;
}Stay between 0.95 and 0.98. Smaller than that and the button looks like it is collapsing. Use the standalone scale property instead of transform: scale(), so the press never overwrites a transform the component already uses for positioning.
Hover should be close to instant
A hover effect answers the question "can I click this?" The answer is needed now, not in half a second. Keep hover color changes at 150ms or less, and consider no transition at all for dense lists where the pointer sweeps across many rows.
Menus appear instantly
Context menus, dropdowns and command palettes are opened constantly by people who already know what they want. An entrance animation there is a delay you charge them every time. Show the menu immediately and only animate the exit, which covers the jump without slowing anyone down.
.menu {
opacity: 1;
transition: none; /* opening is immediate */
}
.menu[data-closing] {
opacity: 0;
transition: opacity 120ms ease-out;
}Nothing moves on page load
If a toast slides in, a list staggers up or a counter rolls every time the page renders, then every refresh, every back button and every route change replays it. That is not delight; it is noise, and it makes the interface feel slower than it is.
Animate in response to something the person did. In React with Motion, initial={false} renders the first state without animating, and only later changes transition:
<AnimatePresence initial={false}>
{items.map((item) => (
<motion.li key={item.id} exit={{ opacity: 0 }} />
))}
</AnimatePresence>A quick timing guide
- Hover and focus feedback: 0 to 150ms.
- Press feedback: 100 to 200ms.
- Small UI entering (tooltips, popovers): 150 to 250ms.
- Larger surfaces (sheets, dialogs): 250 to 400ms.
- Anything used many times a minute: as close to zero as you can get away with.
Checklist
- Buttons scale to between 0.95 and 0.98 when pressed.
- Hover transitions are 150ms or less.
- Frequently used menus open instantly and animate only on close.
- Nothing animates on first render unless it is the point of the page.
- Motion respects
prefers-reduced-motion.
This piece expands on the Motion rules in my cheat sheet, where every rule has a live example.
Keep readingAnimating entrances and exitsWhere things should grow from, why leaving should be quieter than arriving, and how to bring content in without it feeling heavy.