Tooltip
Displays informative text when users hover over or focus on an element
Usage
import { Tooltip } from '@lenso/ui';"use client";
// Adapted from HeroUI v3.2.6 tooltip-basic (Apache-2.0).import { CircleInfo } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Tooltip } from "@lenso/ui";import { useId } from "react";
const styles = stylex.create({ row: { display: "flex", alignItems: "center", gap: 16 } });
export function TooltipBasic() { const textId = useId(); const informationId = useId(); return ( <Tooltip.Provider delay={0}> <div {...stylex.props(styles.row)}> <Tooltip> <Tooltip.Trigger aria-describedby={textId} render={<Button variant="secondary" />}> Hover me </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={textId}>This is a tooltip</Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> <Tooltip> <Tooltip.Trigger aria-describedby={informationId} render={<Button isIconOnly aria-label="More information" variant="tertiary" />} > <CircleInfo /> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={informationId}>More information</Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> </div> </Tooltip.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Anatomy
import { Tooltip, Button } from '@lenso/ui';
export default () => ( <Tooltip> <Tooltip.Trigger> <Button>Hover for tooltip</Button> </Tooltip.Trigger> <Tooltip.Content> <Tooltip.Arrow /> Helpful information about this element </Tooltip.Content> </Tooltip>)Examples
Placement
"use client";
// Adapted from HeroUI v3.2.6 tooltip-placement (Apache-2.0).import { Button, Tooltip } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { useId } from "react";import { tokens } from "@lenso/tokens/tokens.stylex.const";
const styles = stylex.create({ grid: { display: "grid", gridTemplateColumns: "repeat(3, minmax(0, 1fr))", gap: 16 }, center: { display: "flex", alignItems: "center", justifyContent: "center" }, caption: { fontSize: 14, color: tokens.muted }, button: { width: "100%" },});
function Placement({ side, label }: { side: "top" | "left" | "right" | "bottom"; label: string }) { const id = useId(); return ( <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={id} render={<Button variant="tertiary" xstyle={styles.button} />} > {label} </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner side={side} sideOffset={7}> <Tooltip.Popup id={id}> <Tooltip.Arrow /> <p>{label} placement</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> );}
export function TooltipPlacement() { return ( <div {...stylex.props(styles.grid)}> <div /> <Placement side="top" label="Top" /> <div /> <Placement side="left" label="Left" /> <div {...stylex.props(styles.center)}> <span {...stylex.props(styles.caption)}>Hover buttons</span> </div> <Placement side="right" label="Right" /> <div /> <Placement side="bottom" label="Bottom" /> <div /> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
With Arrow
"use client";
// Adapted from HeroUI v3.2.6 tooltip-with-arrow (Apache-2.0).import { Button, Tooltip } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { useId } from "react";
const styles = stylex.create({ row: { display: "flex", alignItems: "center", gap: 16 } });
export function TooltipWithArrow() { const arrowId = useId(); const offsetId = useId(); return ( <div {...stylex.props(styles.row)}> <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={arrowId} render={<Button variant="secondary" />} > With Arrow </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner sideOffset={7}> <Tooltip.Popup id={arrowId}> <Tooltip.Arrow /> <p>Tooltip with arrow indicator</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={offsetId} render={<Button variant="primary" />} > Custom Offset </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner sideOffset={12}> <Tooltip.Popup id={offsetId}> <Tooltip.Arrow /> <p>Custom offset from trigger</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Custom Triggers
"use client";
// Adapted from HeroUI v3.2.6 tooltip-custom-trigger (Apache-2.0).import { CircleCheckFill, CircleQuestion } from "@gravity-ui/icons";import { Avatar, Chip, Tooltip } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { useId } from "react";import { tokens } from "@lenso/tokens/tokens.stylex.const";
const ping = stylex.keyframes({ "75%": { transform: "scale(2)", opacity: 0 }, "100%": { transform: "scale(2)", opacity: 0 },});const styles = stylex.create({ row: { display: "flex", alignItems: "center", gap: 24 }, profile: { display: "flex", flexDirection: "column", gap: 0, paddingBlock: 4 }, strong: { fontWeight: 600 }, email: { fontSize: 12, color: tokens.muted }, status: { display: "flex", alignItems: "center", gap: 6 }, dot: { position: "relative", display: "flex", width: 8, height: 8 }, pulse: { position: "absolute", display: "inline-flex", width: "100%", height: "100%", borderRadius: "50%", backgroundColor: tokens.success, opacity: 0.75, animationName: { default: ping, "@media (prefers-reduced-motion: reduce)": "none" }, animationDuration: "1s", animationTimingFunction: "cubic-bezier(0, 0, .2, 1)", animationIterationCount: "infinite", }, dotCenter: { position: "relative", display: "inline-flex", width: 8, height: 8, borderRadius: "50%", backgroundColor: tokens.success, }, iconBackground: { borderRadius: "50%", backgroundColor: tokens.accentSoft, padding: 8 }, icon: { color: tokens.accentSoftForeground }, help: { maxWidth: 320, paddingInline: 4, paddingBlock: 6 }, helpHeading: { marginBottom: 4, fontWeight: 600 }, helpText: { fontSize: 14, color: tokens.muted },});
export function TooltipCustomTrigger() { const avatarId = useId(); const statusId = useId(); const infoId = useId(); return ( <div {...stylex.props(styles.row)}> <Tooltip> <Tooltip.Trigger delay={0} aria-label="User avatar" aria-describedby={avatarId}> <Avatar size="sm"> <Avatar.Image alt="Jane Doe" src="https://img.heroui.chat/image/avatar?w=400&h=400&u=4" /> <Avatar.Fallback>JD</Avatar.Fallback> </Avatar> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner sideOffset={7}> <Tooltip.Popup id={avatarId}> <Tooltip.Arrow /> <div {...stylex.props(styles.profile)}> <p {...stylex.props(styles.strong)}>Jane Doe</p> <p {...stylex.props(styles.email)}>[email protected]</p> </div> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> <Tooltip> <Tooltip.Trigger delay={0} aria-label="Status chip" aria-describedby={statusId}> <Chip color="success"> <CircleCheckFill width={12} /> <Chip.Label>Active</Chip.Label> </Chip> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={statusId} xstyle={styles.status}> <span {...stylex.props(styles.dot)} aria-hidden="true"> <span {...stylex.props(styles.pulse)} /> <span {...stylex.props(styles.dotCenter)} /> </span> <p>Jane is currently online</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> <Tooltip> <Tooltip.Trigger delay={0} aria-label="Info icon" aria-describedby={infoId}> <div {...stylex.props(styles.iconBackground)}> <CircleQuestion {...stylex.props(styles.icon)} /> </div> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner sideOffset={7}> <Tooltip.Popup id={infoId}> <Tooltip.Arrow /> <div {...stylex.props(styles.help)}> <p {...stylex.props(styles.helpHeading)}>Help Information</p> <p {...stylex.props(styles.helpText)}> This is a helpful tooltip with more detailed information about this feature. </p> </div> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Render Function
"use client";
// Adapted from HeroUI v3.2.6 tooltip-render-function (Apache-2.0).import { CircleInfo } from "@gravity-ui/icons";import { Button, Tooltip } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { useId } from "react";
const styles = stylex.create({ row: { display: "flex", alignItems: "center", gap: 16 } });
export function RenderFunction() { const textId = useId(); const informationId = useId(); return ( <div {...stylex.props(styles.row)}> <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={textId} render={<Button variant="secondary" />} > Hover me </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={textId} render={(props) => <div {...props} data-custom="foo" />}> <p>This is a tooltip</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={informationId} render={<Button isIconOnly aria-label="More information" variant="tertiary" />} > <CircleInfo /> </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={informationId} render={(props) => <div {...props} data-custom="foo" />} > <p>More information</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Customization
Tailwind CSS
"use client";
// Adapted from HeroUI v3.2.6 tooltip-custom-styles (Apache-2.0).import { Button, Tooltip } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { useId } from "react";import { tokens } from "@lenso/tokens/tokens.stylex.const";
const styles = stylex.create({ popup: { borderRadius: tokens.radiusLg, borderWidth: 1, borderStyle: "solid", borderColor: "color-mix(in oklab, var(--border) 80%, transparent)", backgroundColor: tokens.surface, paddingInline: 10, paddingBlock: 4, fontSize: 12, color: tokens.foreground, boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", },});
export function CustomStyles() { const id = useId(); return ( <Tooltip> <Tooltip.Trigger delay={0} aria-describedby={id} render={<Button variant="secondary" />}> Share link </Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup id={id} xstyle={styles.popup}> <p>Copied to clipboard</p> </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Global CSS
To customize the Tooltip component classes, you can use the @layer components directive.
Learn more.
@layer components { .tooltip { @apply rounded-xl shadow-lg; }
.tooltip__trigger { @apply cursor-help; }}Styling Reference
HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The Tooltip component uses these CSS classes (View source styles):
Base Classes [!toc]
.tooltip- Base tooltip styles with animations.tooltip__trigger- Trigger element styles
Interactive States
The component supports animation states:
- Entering:
[data-entering]- Applied during tooltip appearance - Exiting:
[data-exiting]- Applied during tooltip disappearance - Placement:
[data-placement="*"]- Applied based on tooltip position
API Reference
Tooltip
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Trigger element and content |
delay | number | 700 | Delay in milliseconds before showing tooltip |
closeDelay | number | 0 | Delay in milliseconds before hiding tooltip |
trigger | "hover" | "focus" | "hover" | How the tooltip is triggered |
isDisabled | boolean | false | Whether the tooltip is disabled |
shouldSkipAnimation | boolean | false | Whether to skip the enter and exit animations when moving quickly from one tooltip to another |
Tooltip.Content
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Content to display in the tooltip |
showArrow | boolean | false | Whether to show the arrow indicator |
offset | number | 3 (7 with arrow) | Distance from the trigger element |
placement | "top" | "bottom" | "left" | "right" (and variants) | "top" | Placement of the tooltip |
className | string | - | Additional CSS classes |
render | DOMRenderFunction<keyof React.JSX.IntrinsicElements, TooltipRenderProps> | - | Overrides the default DOM element with a custom render function. |
Tooltip.Trigger
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Element that triggers the tooltip |
className | string | - | Additional CSS classes |
Tooltip.Arrow
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | Custom arrow element |
className | string | - | Additional CSS classes |
render | DOMRenderFunction<keyof React.JSX.IntrinsicElements, OverlayArrowRenderProps> | - | Overrides the default DOM element with a custom render function. |