# Tooltip Timing

**MUST** · **ID:** `interactions-tooltip-timing` · **Category:** interactions
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/interactions-tooltip-timing

> MUST: Delay first tooltip in a group; subsequent peers no delay

Delay the first tooltip in a group; subsequent peers have no delay

Add a small delay (~500ms) before showing the first tooltip to avoid tooltips appearing on accidental hovers. Once a tooltip in a group is shown, subsequent tooltips in the same area should appear immediately to allow easy exploration.

## Bad — do not do this

`interactions-tooltip-timing-bad`

```tsx
import { useState } from 'react';

export function TooltipTimingBad() {
  const [activeTooltip, setActiveTooltip] = useState<string | null>(null);

  const buttons = [
    { id: 'bold', icon: 'B', label: 'Bold' },
    { id: 'italic', icon: 'I', label: 'Italic' },
    { id: 'underline', icon: 'U', label: 'Underline' },
  ];

  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg p-4">
        <div className="flex gap-1 mb-4">
          {buttons.map((btn) => (
            <div key={btn.id} className="relative">
              <button
                onMouseEnter={() => setActiveTooltip(btn.id)}
                onMouseLeave={() => setActiveTooltip(null)}
                className="w-8 h-8 flex items-center justify-center rounded hover:bg-muted font-medium"
              >
                {btn.icon}
              </button>
              {activeTooltip === btn.id && (
                <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 bg-popover text-popover-foreground text-xs rounded whitespace-nowrap">
                  {btn.label}
                </div>
              )}
            </div>
          ))}
        </div>
        <p className="text-xs text-muted-foreground">
          Tooltips appear instantly. Moving between buttons shows/hides rapidly, causing flicker.
        </p>
      </div>
      <p className="text-xs text-error mt-4">
        No delay - accidental hovers trigger tooltips
      </p>
    </div>
  );
}
```

## Good — do this

`interactions-tooltip-timing-good`

```tsx
import { useState, useRef, useEffect } from 'react';

export function TooltipTimingGood() {
  const [activeTooltip, setActiveTooltip] = useState<string | null>(null);
  const [hasShownTooltip, setHasShownTooltip] = useState(false);
  const timeoutRef = useRef<ReturnType<typeof setTimeout>>();

  const buttons = [
    { id: 'bold', icon: 'B', label: 'Bold' },
    { id: 'italic', icon: 'I', label: 'Italic' },
    { id: 'underline', icon: 'U', label: 'Underline' },
  ];

  const handleMouseEnter = (id: string) => {
    if (timeoutRef.current) clearTimeout(timeoutRef.current);

    // First tooltip has delay, subsequent are instant
    const delay = hasShownTooltip ? 0 : 500;

    timeoutRef.current = setTimeout(() => {
      setActiveTooltip(id);
      setHasShownTooltip(true);
    }, delay);
  };

  const handleMouseLeave = () => {
    if (timeoutRef.current) clearTimeout(timeoutRef.current);
    timeoutRef.current = setTimeout(() => {
      setActiveTooltip(null);
      // Reset after leaving the group for a while
      timeoutRef.current = setTimeout(() => {
        setHasShownTooltip(false);
      }, 300);
    }, 100);
  };

  useEffect(() => {
    return () => {
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
    };
  }, []);

  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg p-4">
        <div className="flex gap-1 mb-4">
          {buttons.map((btn) => (
            <div key={btn.id} className="relative">
              <button
                onMouseEnter={() => handleMouseEnter(btn.id)}
                onMouseLeave={handleMouseLeave}
                className="w-8 h-8 flex items-center justify-center rounded hover:bg-muted font-medium"
              >
                {btn.icon}
              </button>
              {activeTooltip === btn.id && (
                <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 bg-popover text-popover-foreground text-xs rounded whitespace-nowrap">
                  {btn.label}
                </div>
              )}
            </div>
          ))}
        </div>
        <p className="text-xs text-muted-foreground">
          First tooltip has 500ms delay. After that, moving between peers shows tooltips instantly.
        </p>
      </div>
      <p className="text-xs text-success mt-4">
        Initial delay prevents accidental triggers, instant switching after
      </p>
    </div>
  );
}
```

## References

- [Tooltip Design](https://www.nngroup.com/articles/tooltip-guidelines/)
