docs / core / tooltip
Tooltip
a short label that surfaces on hover or focus and says what a thing is: the name of an icon button, a keyboard shortcut, a word too clipped to read. never more than you can take in at a glance.
intensity · recedesrsc · client
provenance
authored in usvalayer
core / primitivesintensity
recedes · safe anywhere, including dense surfacescomposition
icon-only buttons, truncated text, terse toolbar actionsthe trigger's render prop puts it on an existing element instead of nesting a second buttonno interactive content inside. that is a Popovernever the only place information lives. it is a hint, not the labela11y
opens on focus as well as hover · content is portalled withrole="tooltip" and wired to the trigger via aria-describedby · escape dismisses · exit animation honors motion-reducejest-axedependencies
@base-ui/reactlive demo · try it out
customize
contentthe label on hover and focus
sideOffsetgap from the trigger, in pixels
usage
import { Tooltip, TooltipContent, TooltipTrigger } from "usva/primitives/tooltip";
<Tooltip>
<TooltipTrigger render={<Button variant="outline">Hover me</Button>} />
<TooltipContent>Deploys the current branch</TooltipContent>
</Tooltip>props
| prop | type | default | notes |
|---|---|---|---|
| delay | number | — | hover delay in ms before opening. set on TooltipProvider, shared by every tooltip under it. |
| defaultOpen | boolean | — | open on mount, uncontrolled. |
| open | boolean | — | controlled open state. |
| onOpenChange | (open: boolean) => void | — | fires when the open state changes. |
| sideOffset | number | 6 | distance in px between trigger and content, on TooltipContent. |
get it
npx shadcn add https://usva.build/r/tooltip.jsonsource · components/ui/tooltip.tsxexactly what this command copies
"use client";
import { Tooltip as Base } from "@base-ui/react/tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
import { useScopedTheme } from "./use-scoped-theme";
export const TooltipProvider = Base.Provider;
export const Tooltip = Base.Root;
export const TooltipTrigger = Base.Trigger;
export const TooltipContent = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<typeof Base.Popup> & { sideOffset?: number }
>(({ className, sideOffset = 6, children, ...props }, ref) => {
const [probe, theme] = useScopedTheme();
return (
<>
<span ref={probe} hidden />
<Base.Portal>
<Base.Positioner
data-theme={theme}
sideOffset={sideOffset}
className="z-overlay"
>
<Base.Popup
ref={ref}
className={cn(
"rim-light rounded-md border border-border bg-surface-2 px-2.5 py-1.5 font-mono text-[11px] leading-none text-ink shadow-raised",
"transition-enter duration-fast ease-soft motion-reduce:transition-none motion-reduce:transform-none",
"data-[starting-style]:translate-y-0.5 data-[starting-style]:opacity-0",
"data-[ending-style]:translate-y-0.5 data-[ending-style]:opacity-0",
className,
)}
{...props}
>
{children}
</Base.Popup>
</Base.Positioner>
</Base.Portal>
</>
);
});
TooltipContent.displayName = "TooltipContent";