Hover Restraint
Hover states fire constantly. Sweep the cursor across a nav and you can cross ten items in one second. If each item fades in, the interface is always a few frames behind the cursor. It reads as slow, even when nothing is.
A useful rule from Emil Kowalski: the more often an interaction happens, the less animation it can afford. Hover is the most frequent interaction there is, so hover feedback should be instant.
- Inbox
- Today
- Upcoming
- Projects
- Archive
- Inbox
- Today
- Upcoming
- Projects
- Archive
If a background or color change feels too harsh at full speed, keep any transition under 100ms. Past that, people start to feel the delay.
Animate the leave
Instant does not have to mean abrupt. Switch the highlight on with no delay, then let it fade out after the cursor moves away. The feedback stays immediate, and the fading trail behind the cursor adds the polish an enter animation was trying to buy.
- Inbox
- Today
- Upcoming
- Projects
- Archive
- Inbox
- Today
- Upcoming
- Projects
- Archive
Both highlights appear with no delay. On the right, the old highlight fades out behind the cursor, which reads as polish instead of lag.
Tooltips
Tooltips are the exception where a delay helps. Without one, tooltips pop up while people are just passing through. So delay the first tooltip, but once one is open, its neighbors should appear at once.
The same thinking applies beyond the cursor. An action triggered by a keyboard shortcut should not animate either. Shortcut users are your fastest users, and a mandatory transition charges them for speed they already have.
Usage
The pattern from the second demo takes one extra line: give the element a normal transition, then remove it while the element is hovered.
<a class="transition-colors duration-300 hover:duration-0 hover:bg-muted">
Inbox
</a>.item {
transition: background-color 300ms;
}
.item:hover {
background-color: var(--muted);
transition-duration: 0s;
}The test is simple. Sweep the cursor across the component as fast as you can. If highlights are still catching up to where the cursor is, something is animating in when it should simply appear.