HextaUI

Tooltip

Uma dica curta no hover ou no foco do teclado que abre após uma breve pausa, alterna instantaneamente entre vizinhos e exibe atalhos.

import {
  IconArrowBackUp,
  IconArrowForwardUp,
  IconShare2,
  IconTrash,
} from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { Kbd } from "@/components/ui/kbd"
import { TooltipGroup, TooltipTrigger } from "@/components/ui/tooltip"

export function TooltipDemo() {
  return (
    <TooltipGroup>
      <div className="flex items-center gap-1">
        <TooltipTrigger
          content={
            <>
              Undo
              <Kbd keys="mod+z" />
            </>
          }
          render={<Button variant="ghost" size="icon" aria-label="Undo" />}
        >
          <IconArrowBackUp />
        </TooltipTrigger>
        <TooltipTrigger
          content={
            <>
              Redo
              <Kbd keys="mod+shift+z" />
            </>
          }
          render={<Button variant="ghost" size="icon" aria-label="Redo" />}
        >
          <IconArrowForwardUp />
        </TooltipTrigger>
        <TooltipTrigger
          content="Share"
          render={<Button variant="ghost" size="icon" aria-label="Share" />}
        >
          <IconShare2 />
        </TooltipTrigger>
        <TooltipTrigger
          content="Move to trash"
          render={
            <Button variant="ghost" size="icon" aria-label="Move to trash" />
          }
        >
          <IconTrash />
        </TooltipTrigger>
      </div>
    </TooltipGroup>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/tooltip.json

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"
<Tooltip>
  <TooltipTrigger render={<Button variant="ghost" size="icon" aria-label="Undo" />}>
    <IconArrowBackUp />
  </TooltipTrigger>
  <TooltipContent>Undo</TooltipContent>
</Tooltip>

Um tooltip abre após uma breve pausa sobre o gatilho, ou na hora quando o gatilho recebe foco do teclado. Com um aberto, os vizinhos abrem instantaneamente e sem animação, então percorrer uma toolbar parece ler rótulos em vez de esperar por cada um.

Essa troca instantânea funciona entre tooltips que compartilham um TooltipProvider. Envolva seu app (ou uma toolbar) em um para compartilhar o delay. Um tooltip sem provider ainda funciona sozinho com os mesmos padrões.

<TooltipProvider>
  <App />
</TooltipProvider>
TooltipProvider
└── Tooltip
    ├── TooltipTrigger
    └── TooltipContent

TooltipGroup
└── TooltipTrigger

Lados

Defina side em TooltipContent. Os lados lógicos seguem a direção de leitura, e o tooltip inverte quando não há espaço.

import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const sides = [
  { side: "top", cell: "col-start-2 row-start-1" },
  { side: "inline-start", cell: "col-start-1 row-start-2" },
  { side: "inline-end", cell: "col-start-3 row-start-2" },
  { side: "bottom", cell: "col-start-2 row-start-3" },
] as const

export function TooltipSides() {
  return (
    <TooltipProvider>
      <div className="grid grid-cols-3 grid-rows-3 place-items-center gap-2">
        {sides.map(({ side, cell }) => (
          <div key={side} className={cell}>
            <Tooltip>
              <TooltipTrigger render={<Button variant="outline" />}>
                {side}
              </TooltipTrigger>
              <TooltipContent side={side}>{side}</TooltipContent>
            </Tooltip>
          </div>
        ))}
      </div>
    </TooltipProvider>
  )
}

Com seta

arrow adiciona um ponteiro que permanece no gatilho mesmo quando o tooltip se desloca para caber na tela.

import { IconBell } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export function TooltipWithArrow() {
  return (
    <Tooltip>
      <TooltipTrigger
        render={
          <Button variant="outline" size="icon" aria-label="Notifications" />
        }
      >
        <IconBell />
      </TooltipTrigger>
      <TooltipContent arrow>Notifications</TooltipContent>
    </Tooltip>
  )
}

Atalho de teclado

Coloque um Kbd ou KbdGroup depois do rótulo. Ele assume as cores do tooltip e fica no final, mostrando ⌘ em dispositivos Apple e Ctrl nos demais.

import { IconDeviceFloppy } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { KbdGroup } from "@/components/ui/kbd"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export function TooltipShortcut() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline" />}>
          <IconDeviceFloppy data-icon="inline-start" />
          Save
        </TooltipTrigger>
        <TooltipContent>
          Save changes
          <KbdGroup keys="mod+s" size="sm" />
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline" />}>
          Go to inbox
        </TooltipTrigger>
        <TooltipContent>
          Go to inbox
          <KbdGroup keys="g i" size="sm" />
        </TooltipContent>
      </Tooltip>
    </div>
  )
}

Toolbar

Pare em um botão e depois deslize pelo grupo. Tooltips sob um mesmo TooltipProvider trocam instantaneamente enquanto você passa de um para outro.

import {
  IconBold,
  IconItalic,
  IconLink,
  IconStrikethrough,
  IconUnderline,
} from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { ButtonGroup } from "@/components/ui/button-group"
import { Kbd } from "@/components/ui/kbd"
import { TooltipGroup, TooltipTrigger } from "@/components/ui/tooltip"

const tools = [
  { label: "Bold", keys: "mod+b", icon: IconBold },
  { label: "Italic", keys: "mod+i", icon: IconItalic },
  { label: "Underline", keys: "mod+u", icon: IconUnderline },
  { label: "Strikethrough", keys: "mod+shift+x", icon: IconStrikethrough },
  { label: "Insert link", keys: "mod+k", icon: IconLink },
]

export function TooltipToolbar() {
  return (
    <TooltipGroup side="bottom">
      <ButtonGroup aria-label="Formatting">
        {tools.map(({ label, keys, icon: Icon }) => (
          <TooltipTrigger
            key={label}
            content={
              <>
                {label}
                <Kbd keys={keys} size="sm" />
              </>
            }
            render={<Button variant="outline" size="icon" aria-label={label} />}
          >
            <Icon />
          </TooltipTrigger>
        ))}
      </ButtonGroup>
    </TooltipGroup>
  )
}

Desabilitado

Um botão desabilitado ainda pode explicar o motivo com focusableWhenDisabled, que o mantém com hover e na ordem de tabulação. disabled em Tooltip desliga o tooltip.

import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export function TooltipDisabled() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Tooltip>
        <TooltipTrigger render={<Button disabled focusableWhenDisabled />}>
          Publish
        </TooltipTrigger>
        <TooltipContent>Add a title before publishing</TooltipContent>
      </Tooltip>
      <Tooltip disabled>
        <TooltipTrigger render={<Button variant="outline" />}>
          No tooltip
        </TooltipTrigger>
        <TooltipContent>You won’t see this</TooltipContent>
      </Tooltip>
    </div>
  )
}

Conteúdo longo

O texto quebra em uma largura confortável e nunca passa da borda da tela, nem mesmo URLs longas.

import { IconInfoCircle } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export function TooltipLongContent() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline" />}>
          <IconInfoCircle data-icon="inline-start" />
          Retention
        </TooltipTrigger>
        <TooltipContent>
          Deleted projects stay in the trash for 30 days. After that they’re
          removed for good, along with their deployments and logs.
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline" />}>
          Webhook URL
        </TooltipTrigger>
        <TooltipContent>
          https://api.example.com/v1/hooks/8f3a2c91d7e64b0f9a1c5e2d7b8a4f60/deliveries
        </TooltipContent>
      </Tooltip>
    </div>
  )
}

Controlado

Passe open e onOpenChange para controlar o tooltip você mesmo. O motivo diz o que o abriu ou fechou.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export function TooltipControlled() {
  const [open, setOpen] = React.useState(false)
  const [reason, setReason] = React.useState("none")

  return (
    <div className="flex flex-col items-center gap-3">
      <div className="flex items-center gap-2">
        <Tooltip
          open={open}
          onOpenChange={(next, details) => {
            setOpen(next)
            setReason(details.reason)
          }}
        >
          <TooltipTrigger render={<Button variant="outline" />}>
            Hover or focus me
          </TooltipTrigger>
          <TooltipContent>Controlled tooltip</TooltipContent>
        </Tooltip>
        <Button
          variant="ghost"
          onClick={() => {
            setOpen((value) => !value)
            setReason("button")
          }}
        >
          {open ? "Hide" : "Show"}
        </Button>
      </div>
      <p className="text-sm text-muted-foreground">
        Open: {String(open)} · last reason: {reason}
      </p>
    </div>
  )
}

Um tooltip, vários gatilhos

Crie um handle com createTooltipHandle e passe um payload de cada gatilho para compartilhar um único tooltip.

"use client"

import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import {
  createTooltipHandle,
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

type Person = { name: string; initials: string; status: string }

const people: Person[] = [
  { name: "Ada Lovelace", initials: "AL", status: "Online" },
  { name: "Alan Turing", initials: "AT", status: "In a meeting" },
  { name: "Grace Hopper", initials: "GH", status: "Away" },
]

const presence = createTooltipHandle<Person>()

export function TooltipDetached() {
  return (
    <TooltipProvider>
      <div className="flex items-center gap-2">
        {people.map((person) => (
          <TooltipTrigger
            key={person.name}
            handle={presence}
            payload={person}
            aria-label={person.name}
            render={
              <button className="rounded-full outline-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden" />
            }
          >
            <Avatar>
              <AvatarFallback>{person.initials}</AvatarFallback>
            </Avatar>
          </TooltipTrigger>
        ))}
        <Tooltip handle={presence}>
          {({ payload }) => (
            <TooltipContent side="bottom">
              {payload ? `${payload.name} · ${payload.status}` : null}
            </TooltipContent>
          )}
        </Tooltip>
      </div>
    </TooltipProvider>
  )
}

Da direita para a esquerda

Dentro de dir="rtl", inline-end abre à esquerda e o atalho vai para a outra ponta.

import { IconBookmark, IconHeart, IconShare2 } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { Kbd } from "@/components/ui/kbd"
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const actions = [
  { label: "أعجبني", icon: IconHeart },
  { label: "حفظ", icon: IconBookmark, keys: "mod+d" },
  { label: "مشاركة", icon: IconShare2 },
]

export function TooltipRtl() {
  return (
    <div dir="rtl">
      <TooltipProvider>
        <div className="flex items-center gap-1">
          {actions.map(({ label, icon: Icon, keys }) => (
            <Tooltip key={label}>
              <TooltipTrigger
                render={
                  <Button variant="ghost" size="icon" aria-label={label} />
                }
              >
                <Icon />
              </TooltipTrigger>
              <TooltipContent side="inline-end" arrow>
                {label}
                {keys && <Kbd keys={keys} size="sm" />}
              </TooltipContent>
            </Tooltip>
          ))}
        </div>
      </TooltipProvider>
    </div>
  )
}
TeclaAção
TabFocar o gatilho pelo teclado abre seu tooltip na hora.
EscFecha o tooltip e mantém o foco no gatilho.
EnterSpaceAtiva o gatilho e fecha o tooltip, para não cobrir o que acontece em seguida.
  • Um tooltip é uma dica visual, não um rótulo. Dê aos gatilhos apenas com ícone um aria-label que corresponda ao texto do tooltip.
  • Nada abre em toque, e um toque só faz o que o gatilho faz. Não coloque em um tooltip nada de que as pessoas precisem; use um popover para isso.
  • Usuários de ponteiro podem mover o mouse sobre o tooltip para lê-lo ou selecioná-lo sem que ele feche.
  • Com movimento reduzido, o tooltip aparece com fade sem escala.

Construído sobre o tooltip do Base UI. Cada parte aceita as props da primitiva que envolve.

PropTipoPadrão
delayMilissegundos de pausa sobre um gatilho antes de abrir.
number300
closeDelayMilissegundos antes de fechar após sair.
number0
timeoutQuanto tempo depois de um fechar o próximo abre instantaneamente.
number400
PropTipoPadrão
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open, details) => void–
onOpenChangeCompleteChamado após a animação de abertura ou fechamento.
(open) => void–
disabledDesliga o tooltip.
booleanfalse
disableHoverablePopupFecha assim que o ponteiro sai do gatilho.
booleanfalse
trackCursorAxis
"none" | "x" | "y" | "both""none"
handleDe createTooltipHandle.
TooltipHandle<Payload>–
children
ReactNode | ({ payload }) => ReactNode–
PropTipoPadrão
delaySubstitui o delay do provider para este gatilho.
number–
closeDelay
number–
closeOnClick
booleantrue
disabledImpede que este gatilho abra o tooltip. O elemento continua habilitado.
booleanfalse
handle
TooltipHandle<Payload>–
payload
Payload–
render
ReactElement | (props, state) => ReactElement<button>
AtributoDescrição
data-slot="tooltip-trigger"O gatilho.
data-popup-openPresente enquanto seu tooltip está aberto.
data-trigger-disabledPresente quando o gatilho não pode abrir o tooltip.
PropTipoPadrão
side
"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""top"
align
"start" | "center" | "end""center"
sideOffset
number | (data) => number6, or 8 with arrow
alignOffset
number0
arrow
booleanfalse
collisionPadding
number8
arrowPadding
number8
portalPropsComo container ou keepMounted.
TooltipPortal props–
AtributoDescrição
data-slot="tooltip-content"O popup.
data-slot="tooltip-positioner"O wrapper de posicionamento em volta do popup.
data-sideO lado em que abriu depois de inverter.
data-alignO alinhamento.
data-instant"delay", "focus" ou "dismiss" quando abre ou fecha sem animação.
data-starting-stylePresente enquanto a animação de entrada ocorre.
data-ending-stylePresente enquanto a animação de saída ocorre.
--available-widthEspaço entre o gatilho e a borda da tela.
--transform-originO ponto a partir do qual o tooltip escala.

Renderizado para você por arrow. Use diretamente só ao compor sua própria parte de conteúdo.

AtributoDescrição
data-slot="tooltip-arrow"A seta.
data-uncenteredPresente quando não consegue apontar para o centro do gatilho.

Usado em blocos

Blocos que se baseiam em Tooltip.