Skip to content
Lenso UI

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

PropTypeDefaultDescription
childrenReact.ReactNode-Trigger element and content
delaynumber700Delay in milliseconds before showing tooltip
closeDelaynumber0Delay in milliseconds before hiding tooltip
trigger"hover" | "focus""hover"How the tooltip is triggered
isDisabledbooleanfalseWhether the tooltip is disabled
shouldSkipAnimationbooleanfalseWhether to skip the enter and exit animations when moving quickly from one tooltip to another

Tooltip.Content

PropTypeDefaultDescription
childrenReact.ReactNode-Content to display in the tooltip
showArrowbooleanfalseWhether to show the arrow indicator
offsetnumber3 (7 with arrow)Distance from the trigger element
placement"top" | "bottom" | "left" | "right" (and variants)"top"Placement of the tooltip
classNamestring-Additional CSS classes
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, TooltipRenderProps>-Overrides the default DOM element with a custom render function.

Tooltip.Trigger

PropTypeDefaultDescription
childrenReact.ReactNode-Element that triggers the tooltip
classNamestring-Additional CSS classes

Tooltip.Arrow

PropTypeDefaultDescription
childrenReact.ReactNode-Custom arrow element
classNamestring-Additional CSS classes
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, OverlayArrowRenderProps>-Overrides the default DOM element with a custom render function.