Tooltip
A popup that displays contextual information when hovering or focusing on a trigger element.
npx shadcn@latest add @velaui/tooltippnpm dlx velaui add @velaui/tooltipyarn dlx shadcn@latest add @velaui/tooltipbunx --bun shadcn@latest add @velaui/tooltipveni add tooltipInstalls the copy of vela-ui that veni-builder serves to Venizia apps; veni-builder serves the Radix UI copy only, so this tab exists for that base alone.
npx shadcn@latest add @velaui/tooltippnpm dlx velaui add @velaui/tooltipyarn dlx shadcn@latest add @velaui/tooltipbunx --bun shadcn@latest add @velaui/tooltipExamples
- Default
- Accent
- Sides
- Align
- Icon trigger
Usage
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
export default function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover me</Button>
</TooltipTrigger>
<TooltipContent>Add to library</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
export default function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
Hover me
</TooltipTrigger>
<TooltipContent>Add to library</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}Props
Tooltip
Takes every prop of TooltipProps.
TooltipContent
| Prop | Type | Default |
|---|---|---|
arrowPadding | number | 8 |
sideOffset | number | 4 |
variant | "neutral" | "accent" | "neutral" |
Takes these and every prop of TooltipContentProps.
TooltipProvider
| Prop | Type | Default |
|---|---|---|
delayDuration | number | 0 |
Takes these and every prop of TooltipProviderProps.
TooltipTrigger
Takes every prop of TooltipTriggerProps.
Tooltip
Takes every prop of TooltipProps.
TooltipContent
| Prop | Type | Default |
|---|---|---|
align | import("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").Align | "center" |
alignOffset | number | import("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").OffsetFunction | 0 |
arrowPadding | number | 8 |
side | import("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").Side | "top" |
sideOffset | number | 4 |
variant | "neutral" | "accent" | "neutral" |
Takes these and every prop of TooltipContentProps.
TooltipProvider
| Prop | Type | Default |
|---|---|---|
delay | number | 0 |
Takes these and every prop of TooltipProviderProps.
TooltipTrigger
Takes every prop of TooltipTriggerProps.
Dependencies
- npm
class-variance-authority@0.7.1radix-ui@1.6.0- Installs with it
- nothing else
- npm
@base-ui/react@1.6.0class-variance-authority@0.7.1- Installs with it
- nothing else
Source
components/ui/tooltip.tsx
/* eslint-disable react-refresh/only-export-components */
"use client";
import * as React from "react";
import { Tooltip as TooltipPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const tooltipContentVariants = cva(
"group/tooltip z-50 flex w-fit max-w-[240px] origin-(--radix-tooltip-content-transform-origin) flex-col items-start justify-center gap-1.5 rounded-lg px-2 py-1 text-xs leading-[1.3] font-medium has-data-[slot=kbd]:pr-1 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
{
variants: {
variant: {
neutral: "bg-bg-foreground text-text-inverse",
accent: "bg-accent-default text-text-strong",
},
},
defaultVariants: {
variant: "neutral",
},
},
);
export type TooltipProviderProps = React.ComponentProps<typeof TooltipPrimitive.Provider>;
function TooltipProvider({
delayDuration = 0,
...props
}: TooltipProviderProps) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delayDuration={delayDuration}
{...props}
/>
);
}
export type TooltipProps = React.ComponentProps<typeof TooltipPrimitive.Root>;
function Tooltip({
...props
}: TooltipProps) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}
export type TooltipTriggerProps = React.ComponentProps<typeof TooltipPrimitive.Trigger>;
function TooltipTrigger({
...props
}: TooltipTriggerProps) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
export type TooltipContentProps = React.ComponentProps<
typeof TooltipPrimitive.Content
> &
VariantProps<typeof tooltipContentVariants>;
function TooltipContent({
className,
sideOffset = 4,
// The design insets the arrow 8px from the start/end edge of the bubble.
arrowPadding = 8,
variant = "neutral",
children,
...props
}: TooltipContentProps) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
data-variant={variant}
sideOffset={sideOffset}
arrowPadding={arrowPadding}
className={cn(tooltipContentVariants({ variant }), className)}
{...props}
>
{children}
<TooltipPrimitive.Arrow
width={12}
height={6}
className="z-50 fill-bg-foreground group-data-[variant=accent]/tooltip:fill-accent-default"
/>
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
}
export {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
tooltipContentVariants,
};components/ui/tooltip.tsx
/* eslint-disable react-refresh/only-export-components */
"use client";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const tooltipContentVariants = cva(
"group/tooltip z-50 flex w-fit max-w-[240px] origin-(--transform-origin) flex-col items-start justify-center gap-1.5 rounded-lg px-2 py-1 text-xs leading-[1.3] font-medium has-data-[slot=kbd]:pr-1 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
{
variants: {
variant: {
neutral: "bg-bg-foreground text-text-inverse",
accent: "bg-accent-default text-text-strong",
},
},
defaultVariants: {
variant: "neutral",
},
},
);
export type TooltipProviderProps = TooltipPrimitive.Provider.Props;
function TooltipProvider({
delay = 0,
...props
}: TooltipProviderProps) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delay={delay}
{...props}
/>
);
}
export type TooltipProps = TooltipPrimitive.Root.Props;
function Tooltip({ ...props }: TooltipProps) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}
export type TooltipTriggerProps = TooltipPrimitive.Trigger.Props;
function TooltipTrigger({ ...props }: TooltipTriggerProps) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
export type TooltipContentProps = TooltipPrimitive.Popup.Props &
Pick<
TooltipPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "arrowPadding"
> &
VariantProps<typeof tooltipContentVariants> & {
/** Distance from the trigger to the arrow's tip, in px (Radix's meaning). */
sideOffset?: number;
};
const ARROW_HEIGHT = 6;
function TooltipContent({
className,
side = "top",
align = "center",
alignOffset = 0,
sideOffset = 4,
// The design insets the arrow 8px from the start/end edge of the bubble.
arrowPadding = 8,
variant = "neutral",
children,
...props
}: TooltipContentProps) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Positioner
side={side}
align={align}
alignOffset={alignOffset}
// Radix counts the arrow into the offset; Base UI measures from the bubble's edge.
sideOffset={sideOffset + ARROW_HEIGHT}
arrowPadding={arrowPadding}
className="isolate z-50"
>
<TooltipPrimitive.Popup
data-slot="tooltip-content"
data-variant={variant}
className={cn(tooltipContentVariants({ variant }), className)}
{...props}
>
{children}
{/* Radix's arrow: a 12 x 6 triangle hung off the bubble's edge, turned to face the trigger. */}
<TooltipPrimitive.Arrow className="z-50 h-1.5 w-3 data-[side=bottom]:top-0 data-[side=bottom]:origin-[center_0] data-[side=bottom]:rotate-180 data-[side=left]:right-0 data-[side=left]:origin-[100%_0] data-[side=left]:[transform:translateY(50%)_rotate(-90deg)_translateX(50%)] data-[side=right]:left-0 data-[side=right]:origin-[0_0] data-[side=right]:[transform:translateY(50%)_rotate(90deg)_translateX(-50%)] data-[side=top]:bottom-0 data-[side=top]:translate-y-full">
<svg
width={12}
height={ARROW_HEIGHT}
viewBox="0 0 30 10"
preserveAspectRatio="none"
className="block fill-bg-foreground group-data-[variant=accent]/tooltip:fill-accent-default"
>
<polygon points="0,0 30,0 15,10" />
</svg>
</TooltipPrimitive.Arrow>
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
);
}
export {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
tooltipContentVariants,
};