Back to Dictionary
Onboarding & Tours
●○○Ready to Use
Recommended default

Tooltip Fade & Nudge

A tiny popover label that slides exactly 4-8px outwards from its anchor element while fading in. Used to label icon-only buttons.

Use

Icon-only action buttons (e.g., Share, Delete, Edit)

Avoid

Critical form instructions (put them inline instead)

Safer Alternative

No safer default listed.

Risk Level

Low risk

Live Demo

What It Is

A tiny popover label that slides exactly 4-8px outwards from its anchor element while fading in. Used to label icon-only buttons.

When to Use

  • Icon-only action buttons (e.g., Share, Delete, Edit)
  • Glossary definition hovers
  • Charts where hovering over a bar reveals exact metrics

When NOT to Use

  • Critical form instructions (put them inline instead)
  • Mobile apps (tooltips don't work well on touch)

Configuration Tips

  • 01Initial state should be tucked slightly against the anchor (e.g., y: 4px for a top tooltip)
  • 02Use an instant/very fast duration (100-150ms) to ensure it appears before the user moves away

You've Seen It In

Radix UILinearNotion

AI Implementation Prompts

Move from a fast scaffold to production details, then tune timing and edge states.

Add a tooltip to this icon that drops down slightly and fades in when I hover over the icon.

Reference Implementation

The same implementation approach used by the live preview, kept compact enough to inspect and adapt.

'use client';
import { motion, AnimatePresence } from 'framer-motion';
import { useState, useEffect } from 'react';
import { Info } from 'lucide-react';

export function TooltipFadeNudgeDemo({ isPlaying = false }: { isPlaying?: boolean }) {
    const [isHovered, setIsHovered] = useState(false);

    useEffect(() => {
        if (!isPlaying) {
            setIsHovered(false);
            return;
        }
        const interval = setInterval(() => {
            setIsHovered((prev) => !prev);
        }, 1500);
        return () => clearInterval(interval);
    }, [isPlaying]);

    return (
        <div className="flex h-64 w-full items-center justify-center rounded-2xl bg-stone-50 p-6 shadow-sm ring-1 ring-stone-200">
            <div className="relative flex items-center justify-center">

                {"text-stone-400 italic">/* Anchor element */}
                <button
                    className={`flex h-10 w-10 items-center justify-center rounded-full transition-colors ${isHovered ? 'bg-stone-200' : 'bg-white shadow-sm ring-1 ring-stone-200'
                        }`}
                    onMouseEnter={() => setIsHovered(true)}
                    onMouseLeave={() => setIsHovered(false)}
                >
                    <Info className="h-5 w-5 text-stone-500" />
                </button>

                {"text-stone-400 italic">/* Tooltip Nudging Upwards */}
                <AnimatePresence>
                    {isHovered && (
                        <motion.div
                            initial={{ opacity: 0, y: -4, scale: 0.95 }}
                            animate={{ opacity: 1, y: 0, scale: 1 }}
                            exit={{ opacity: 0, y: -2, scale: 0.95 }}
                            transition={{ duration: 0.15, ease: 'easeOut' }}
                            className="absolute -top-10 flex items-center justify-center rounded bg-stone-900 px-3 py-1.5 text-xs font-medium text-white shadow-md z-10"
                        >
                            Info
                            {"text-stone-400 italic">/* Tooltip Arrow pointing down */}
                            <div className="absolute -bottom-1 left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 bg-stone-900" />
                        </motion.div>
                    )}
                </AnimatePresence>

            </div>
        </div>
    );
}