Tooltips Need a Delay, and Then They Need to Skip It
Tooltips Need a Delay, and Then They Need to Skip It
By Abhishek Jakhar | August 13, 2026
On the question listing pages of my platform, FrontPrep, I include company logos alongside interview questions. To provide more context, I implemented tooltips that reveal the company's name when a user hovers over the logo.
Initially, I encountered a UX friction point: if a user simply swept their cursor across the page, tooltips would pop up instantly for every single logo they passed.
The culprit was a 0ms delay. Instant tooltips are great Actually, instant tooltips are annoying when they trigger accidentally.
To remedy this, I introduced a transition delay of . While this stopped the "pop-up storm," it introduced a new problem: sluggishness. In rows where multiple logos are grouped together, moving from one company to the next required waiting that same every single time.
The goal is to find a balance: prevent accidental triggers during a sweep, but allow rapid browsing once the user has intentionally engaged with the tooltips.

The Proposed Solution
I developed a system based on the concept of "Warm" and "Cold" states. This pattern is common in browser toolbars but often overlooked in custom web development.
The Three-Part Logic
- The Initial Wait: When the page is "cold," the first hover triggers a timer.
- The Warm State: Once a tooltip opens, the page becomes "warm." Any subsequent hovers on other logos trigger tooltips instantly, skipping the delay and animation.
- The Cooldown: Once the cursor leaves a logo, a cooldown timer starts. If the timer expires without another hover, the page returns to a "cold" state.
State Transition Flow
Comparison Table: Cold vs. Warm
| Feature | Cold State | Warm State |
|---|---|---|
| Trigger Delay | ||
| Entrance Animation | Standard | Skipped (Instant) |
| User Intent | Passive/Sweeping | Active/Browsing |
Technical Implementation
In FrontPrep, I use Radix and Motion, but for clarity, I'll demonstrate this using a simplified React approach.
Implementation Checklist
- Create a
TooltipProviderfor shared state. - Implement
onMouseEnterlogic with conditional timers. - Create a
showfunction to handle "warmth" and CSS flags. - Implement
onMouseLeaveto handle cooldowns.
Step 1: Handling the Hover (onMouseEnter)
When the cursor enters a logo, we check the global state via a context variable tooltips.
function handleEnter() {
// Check if the page is already 'warm'
if (tooltips.isWarm) {
show();
return;
}
// If cold, start the delay timer
openTimer.current = setTimeout(show, tooltips.openDelay);
}
Step 2: Opening the Tooltip
The show function manages the visibility and the "instant" visual flag.
function show() {
setInstant(tooltips.isWarm); // Set flag for CSS
setOpen(true);
tooltips.markOpened(); // Set page to warm
}
To ensure the tooltip appears instantly without a sliding or fading animation when warm, I use a CSS data attribute:
/* Skip animation if the 'instant' flag is true */
.tooltip [data-instant="true"] {
transition-duration: 0ms;
}
Step 3: Leaving the Logo (onMouseLeave)
This step is crucial for preventing tooltips from appearing after the cursor has already moved past the element.
function handleLeave() {
clearTimeout(openTimer.current); // Cancel pending opens
if (!open) return; // Exit if tooltip never actually opened
setOpen(false);
setInstant(false);
tooltips.markClosed(); // Start the cooldown
}
Step 4: The Provider Logic
The TooltipProvider acts as the brain, managing the warm reference and the cooldown timer.
const warm = useRef(false);
const cooldownTimer = useRef(null);
useEffect(() => () => clearTimeout(cooldownTimer.current), []);
const tooltips = useMemo(() => ({
openDelay,
isWarm: () => skipWhenWarm ? warm.current : warm.current,
markOpened() {
warm.current = true;
clearTimeout(cooldownTimer.current); // Cancel cooldown if we re-enter
},
markClosed() {
clearTimeout(cooldownTimer.current);
cooldownTimer.current = setTimeout(() => {
warm.current = false; // Return to cold state
}, warmFor);
},
}), [openDelay, warmFor, skipWhenWarm]);
Final Thoughts
By implementing this "Warm/Cold" logic, the interface feels intuitive. It ignores the "noise" of a cursor sweeping across the screen but responds with lightning speed once the user begins actively exploring the data.
Written by Abhishek Jakhar for Master.dev
