DEV Community

Mauro Damian Perez Garcia
Mauro Damian Perez Garcia

Posted on

Delay Is a Design Material

I read a short argument this week that tooltips need a delay before they appear, and then, once you are obviously working your way along a toolbar, they need to drop that delay entirely. It is a tiny piece of interface behavior. It stayed with me longer than most architecture posts I read this month.

Partly because it is correct, and partly because it is not really about tooltips. It is about the fact that timing is something you design, the same way you design spacing or color. Most teams treat it as a leftover.

We pick 200ms because it felt fine on a fast laptop. Or we pick zero because zero seems honest. Or we inherit whatever number shipped inside the component library we installed on day one and never revisit it. Then the product feels twitchy or sluggish, and the bug report says "it feels weird," which is the hardest class of bug there is.

Two different users living in the same hands

What makes the tooltip case interesting is that a single person switches modes mid-interaction.

When my cursor is crossing the screen on its way somewhere else, a tooltip that fires instantly is noise. It flashes, it covers content, it makes the interface feel jumpy for no reason. The delay exists to filter accidental passes.

But the moment I stop and read one tooltip, I have declared intent. I am now surveying. If the next four icons each make me wait 500ms, the interface is punishing me for exactly the behavior it was trying to encourage. The delay was a filter for accidents, and I stopped having accidents.

So the right behavior is stateful: wait at first, then trust me until I leave the neighborhood. That is the whole insight, and it generalizes further than hover states.

The same pattern, wearing other clothes

Once you see it, this shape is everywhere:

  • Autocomplete that should debounce while you are typing a word, then feel instant once you have paused and are clearly evaluating results.
  • Confirmation dialogs that make sense the first time you delete something and become a wall when you are cleaning up thirty items.
  • Retry backoff that protects a struggling service but should collapse quickly once the service is clearly healthy again.
  • Loading spinners that should not appear at all under about 300ms, because a flashed spinner reads as a glitch rather than as progress.

In every case the naive fix is a single constant, and the good fix is a small state machine that reflects what the user or the caller is currently doing. Not complicated. Just deliberate.

Why we skip this

I think there are two reasons, and neither is laziness.

The first is that timing behavior is nearly invisible in code review. A diff that changes delay: 0 to delay: 400 looks like nothing. There is no diagram, no type signature, no failing test. Reviewers approve it in two seconds because there is nothing to hold onto.

The second is that we test interfaces in the wrong conditions. We test them on our own machines, with our own muscle memory, in the specific flow we just built. We do not test them while distracted, on a trackpad, at the end of a long day, in the fifth repetition of a boring task. That is where timing either helps or grates.

Designers often catch this and get overruled, because "it's just a hover delay" sounds unserious next to a migration or a latency budget. But these details are the texture of the product. They are the difference between a tool that feels like an extension of your hand and one you have to consciously operate.

What I try to do now

My rough rule is that any timing constant in the codebase deserves a name and a sentence.

Not a paragraph of documentation. Just enough that the next person knows what the number is protecting against. HOVER_INTENT_DELAY with a note that it filters cursor pass-through tells a reviewer what to think about. A bare 400 tells them nothing, so they will change it based on vibes and someone will change it back six months later.

Then I ask whether the constant should stay constant. If the answer is "it depends on what the user just did," that is the signal to add the tiny bit of state. Usually it is a few lines. Usually it is the difference people actually feel.

The broader belief underneath all of this: latency is not only a performance concern. Sometimes the fastest thing is not the best thing, and the right amount of waiting is a feature you chose on purpose.

Interfaces happen in time. Design that dimension too.

Top comments (0)