Componentfeedback0.88 KB gzipZero dependencies

Tooltip

Floating helper tooltip on hover and keyboard focus.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add tooltip

Or install the full npm package:

npm install bigbullui
import { Tooltip } from "@/components/ui/tooltip";

<Tooltip content="Helpful text">
  <Button variant="outline">Hover me</Button>
</Tooltip>

Usage guidance

Use Tooltip when a ticket stub surface needs Floating helper tooltip on hover and keyboard focus. Keep props minimal and prefer composition with sibling stubs.

Avoid Tooltip for long form flows or dense data grids where a dedicated layout block fits better.

Source code

tooltip.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export type TooltipProps = React.HTMLAttributes<HTMLSpanElement> & {
  content: React.ReactNode;
  side?: "top" | "bottom";
  delayMs?: number;
  /** Dual-mode: false renders instantly with no motion. Default true. */
  animated?: boolean;
};

export function Tooltip({ content, side = "top", delayMs = 300, animated = true, className, children, ...props }: TooltipProps) {
  const [visible, setVisible] = React.useState(false);
  const [mounted, setMounted] = React.useState(false);
  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
  const descriptionId = React.useId();

  React.useEffect(() => {
    setMounted(true);
    return () => {
      if (timer.current) clearTimeout(timer.current);
    };
  }, []);

  const show = () => {
    timer.current = setTimeout(() => setVisible(true), delayMs);
  };
  const hide = () => {
    if (timer.current) clearTimeout(timer.current);
    setVisible(false);
  };

  return (
    <span
      className={cn("relative inline-flex", className)}
      onMouseEnter={show}
      onMouseLeave={hide}
      onFocus={show}
      onBlur={hide}
      {...props}
    >
      <span aria-describedby={mounted && visible ? descriptionId : undefined}>{children}</span>
      {visible && (
        <span
          role="tooltip"
          id={descriptionId}
          className={cn(
            "pointer-events-none absolute left-1/2 z-50 w-max max-w-64 -translate-x-1/2 rounded-md border border-foreground bg-primary px-3 py-1.5 font-mono text-[11px] uppercase tracking-[0.12em] text-primary-foreground",
            animated && "animate-[scale-in_0.15s_ease-out_both] motion-reduce:animate-none",
            side === "top" ? "bottom-[calc(100%+6px)]" : "top-[calc(100%+6px)]"
          )}
        >
          {content}
        </span>
      )}
    </span>
  );
}

Props

PropTypeDescription
contentReact.ReactNodeTooltip text or content.
side"top" | "bottom"Placement of the tooltip.
delayMsnumberDelay before showing (default 300).