logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·03.11.26
← Back to news

Tooltips Need a Delay, and Then They Need to Skip It

Original article
blog.master.dev|20 points|5 comments|by ibobev|Aug 25, 2026

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 →\rightarrow→ Actually, instant tooltips are annoying when they trigger accidentally.

To remedy this, I introduced a transition delay of t=200mst = 200\text{ms}t=200ms. 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 200ms200\text{ms}200ms 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.

Tooltip Context


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

  1. The Initial Wait: When the page is "cold," the first hover triggers a 200ms200\text{ms}200ms timer.
  2. 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.
  3. The Cooldown: Once the cursor leaves a logo, a 300ms300\text{ms}300ms 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

FeatureCold StateWarm State
Trigger Delay200ms200\text{ms}200ms0ms0\text{ms}0ms
Entrance AnimationStandardSkipped (Instant)
User IntentPassive/SweepingActive/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 TooltipProvider for shared state.
  • Implement onMouseEnter logic with conditional timers.
  • Create a show function to handle "warmth" and CSS flags.
  • Implement onMouseLeave to 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.

Author Written by Abhishek Jakhar for Master.dev

Master.dev Logo