Tooltip
A popup that displays helpful information when hovering or focusing an element.
ui/tooltip.tsx
"use client";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
const TooltipProvider = ({
delayDuration,
delay = delayDuration ?? 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider> & {
delayDuration?: number;
}) => <TooltipPrimitive.Provider data-slot="tooltip-provider" delay={delay} {...props} />;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = ({
asChild = false,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger> & {
asChild?: boolean;
}) => {
const render =
asChild && React.isValidElement(children) ? (children as React.ReactElement) : undefined;
return (
<TooltipPrimitive.Trigger data-slot="tooltip-trigger" render={render} {...props}>
{asChild ? null : children}
</TooltipPrimitive.Trigger>
);
};
type TooltipContentProps = React.ComponentProps<typeof TooltipPrimitive.Popup> &
Pick<
React.ComponentProps<typeof TooltipPrimitive.Positioner>,
"align" | "alignOffset" | "side" | "sideOffset"
> & {
asChild?: boolean;
};
const TooltipContent = ({
asChild = false,
className,
side = "top",
sideOffset = 8,
align = "center",
alignOffset = 0,
children,
...props
}: TooltipContentProps) => {
const render =
asChild && React.isValidElement(children) ? (children as React.ReactElement) : undefined;
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Positioner
align={align}
alignOffset={alignOffset}
className="isolate z-110"
side={side}
sideOffset={sideOffset}
>
<TooltipPrimitive.Popup
className={cn(
"fade-in-0 zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-110 origin-(--transform-origin) animate-in rounded-xl bg-primary px-3 py-2 font-normal font-sans text-primary-foreground text-sm shadow-soft ring-1 ring-border data-closed:animate-out motion-reduce:animate-none",
className,
)}
data-slot="tooltip-content"
render={render}
{...props}
>
{asChild ? null : children}
{/* The clip-path half-square comes from creator-frontend, with the tip radius taken
from 3px to 5px because 3px on a 10px arrow still reads as a point at 1x. Radix
rotates its arrow per side; Base UI never does, so the per-side rotation has to
be spelled out here or the tip only points the right way on side="top". */}
<TooltipPrimitive.Arrow className="pointer-events-none absolute size-2.5 rounded-bl-[5px] border border-border bg-primary [clip-path:polygon(0_100%,0_0,100%_100%)] data-[side=bottom]:top-0 data-[side=left]:right-0 data-[side=top]:bottom-0 data-[side=right]:left-0 data-[side=left]:translate-x-1/2 data-[side=right]:-translate-x-1/2 data-[side=bottom]:-translate-y-1/2 data-[side=top]:translate-y-1/2 data-[side=bottom]:rotate-135 data-[side=left]:-rotate-135 data-[side=right]:rotate-45 data-[side=top]:-rotate-45" />
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
);
};
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
- Interaction
- modaltooltip
DependenciesBundle weight · light
- ui-primitives
- @base-ui/react