ComponentsCSS
Tooltip
Small label on hover and focus, with a shared delay across the page.
Installation
npx shadcn@latest add https://ui-collections.vercel.app/r/tide/tooltip.jsonAlso installs the theme, cn(). Packages: clsx, tailwind-merge, radix-ui.
Usage
tooltip-demo.tsx
"use client";
import { Button } from "@/registry/kit/ui/button";
import { SimpleTooltip, TooltipProvider } from "@/registry/kit/ui/tooltip";
const actions = [
{ label: "Redeploy", path: "M4 12a8 8 0 0 1 13.7-5.6M20 12a8 8 0 0 1-13.7 5.6M18 3v4h-4M6 21v-4h4" },
{ label: "Copy link", path: "M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1" },
{ label: "Delete", path: "M5 7h14M10 7V5h4v2M7 7l1 12h8l1-12" },
];
export default function TooltipDemo() {
return (
<TooltipProvider>
<div className="surface flex gap-1 rounded-control p-1.5">
{actions.map((a) => (
<SimpleTooltip key={a.label} content={a.label}>
<Button variant="ghost" size="icon" aria-label={a.label}>
<svg viewBox="0 0 24 24" className="size-4" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<path d={a.path} />
</svg>
</Button>
</SimpleTooltip>
))}
</div>
</TooltipProvider>
);
}API
<TooltipProvider>componentWrap once; sets a 250 ms delay.
<SimpleTooltip content side>componentOne trigger, one label.
Source
components/ui/tooltip.tsx
"use client";
import { Tooltip as TooltipPrimitive } from "radix-ui";
import type { ComponentProps } from "react";
import { cn } from "@/registry/kit/lib/utils";
/** Wrap the app (or a region) once so tooltips share a delay. */
export function TooltipProvider({ delayDuration = 250, ...props }: ComponentProps<typeof TooltipPrimitive.Provider>) {
return <TooltipPrimitive.Provider delayDuration={delayDuration} {...props} />;
}
export const Tooltip = TooltipPrimitive.Root;
export const TooltipTrigger = TooltipPrimitive.Trigger;
export function TooltipContent({ className, sideOffset = 8, ...props }: ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
sideOffset={sideOffset}
className={cn(
"surface-raised z-50 max-w-64 rounded-control px-3.5 py-1.5 text-xs text-fg",
"origin-(--radix-tooltip-content-transform-origin) transition-[opacity,scale] duration-150 ease-theme starting:scale-90 starting:opacity-0 data-[state=closed]:animate-pop-out",
className,
)}
{...props}
/>
</TooltipPrimitive.Portal>
);
}
/** Shorthand for the common case: one trigger, one line of text. */
export function SimpleTooltip({
content,
children,
side,
}: {
content: React.ReactNode;
children: React.ReactNode;
side?: ComponentProps<typeof TooltipPrimitive.Content>["side"];
}) {
return (
<Tooltip>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent side={side}>{content}</TooltipContent>
</Tooltip>
);
}