HextaUI

Tooltip

Ein kurzer Hinweis bei Hover oder Tastaturfokus, der sich nach kurzer Ruhezeit öffnet, zwischen benachbarten Elementen sofort wechselt und Tastenkürzel anzeigt.

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

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

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>

Ein Tooltip öffnet nach kurzem Verweilen auf dem Trigger oder sofort, wenn der Trigger Tastaturfokus erhält. Sobald einer angezeigt wird, öffnen seine Nachbarn sofort und ohne Animation, sodass das Durchgehen einer Toolbar wie das Lesen von Labels wirkt, statt auf jedes zu warten.

Dieser sofortige Wechsel funktioniert zwischen Tooltips, die sich einen TooltipProvider teilen. Umschließe deine App (oder eine Toolbar) mit einem, um die Verzögerung zu teilen. Ein Tooltip ohne Provider funktioniert weiterhin allein mit denselben Standardwerten.

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

TooltipGroup
└── TooltipTrigger

Seiten

Setze side an TooltipContent. Logische Seiten folgen der Leserichtung, und der Tooltip klappt um, wenn kein Platz ist.

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>
  )
}

Mit einem Pfeil

arrow fügt einen Zeiger hinzu, der auf dem Trigger bleibt, auch wenn sich der Tooltip verschiebt, um auf den Bildschirm zu passen.

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>
  )
}

Tastenkürzel

Setze ein Kbd oder KbdGroup nach das Label. Es übernimmt die Farben des Tooltips und sitzt am Ende und zeigt ⌘ auf Apple-Geräten und sonst Strg.

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

Verweile auf einem Button und gleite dann an der Gruppe entlang. Tooltips unter einem TooltipProvider wechseln sofort, während du dich zwischen ihnen bewegst.

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>
  )
}

Deaktiviert

Ein deaktivierter Button kann mit focusableWhenDisabled trotzdem erklären, warum, was ihn hoverbar und in der Tab-Reihenfolge hält. disabled an Tooltip schaltet den Tooltip ab.

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>
  )
}

Langer Inhalt

Text bricht in angenehmer Breite um und läuft nie über den Bildschirmrand, auch bei langen URLs.

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>
  )
}

Kontrolliert

Übergib open und onOpenChange, um den Tooltip selbst zu steuern. Der Grund sagt dir, was ihn geöffnet oder geschlossen hat.

"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>
  )
}

Ein Tooltip, viele Trigger

Erstelle mit createTooltipHandle ein Handle und übergib von jedem Trigger einen payload, um einen einzelnen Tooltip zu teilen.

"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>
  )
}

Rechts nach links

In dir="rtl" öffnet inline-end links, und das Tastenkürzel wandert ans andere Ende.

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>
  )
}
TasteAktion
TabDas Fokussieren des Triggers per Tastatur öffnet seinen Tooltip sofort.
EscSchließt den Tooltip und lässt den Fokus auf dem Trigger.
EnterSpaceAktiviert den Trigger und schließt den Tooltip, damit er nicht verdeckt, was als Nächstes passiert.
  • Ein Tooltip ist ein visueller Hinweis, kein Label. Gib Triggern nur mit Icon ein aria-label, das dem Tooltip-Text entspricht.
  • Bei Touch öffnet sich nichts, und ein Tippen tut nur, was der Trigger tut. Setze nichts in einen Tooltip, was Leute brauchen; nutze dafür ein Popover.
  • Zeigernutzer können auf den Tooltip fahren, um ihn zu lesen oder zu markieren, ohne dass er schließt.
  • Bei aktivierter reduzierter Bewegung blendet der Tooltip ohne Skalierung ein.

Gebaut auf dem Base UI Tooltip. Jeder Teil akzeptiert die Props des Primitivs, das er umschließt.

PropTypStandard
delayMillisekunden, die auf einem Trigger verweilt wird, bevor er öffnet.
number300
closeDelayMillisekunden bis zum Schließen nach dem Verlassen.
number0
timeoutWie lange nach dem Schließen eines Tooltips der nächste sofort öffnet.
number400
PropTypStandard
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open, details) => void–
onOpenChangeCompleteWird nach der Öffnen- oder Schließen-Animation aufgerufen.
(open) => void–
disabledSchaltet den Tooltip ab.
booleanfalse
disableHoverablePopupSchließt, sobald der Zeiger den Trigger verlässt.
booleanfalse
trackCursorAxis
"none" | "x" | "y" | "both""none"
handleVon createTooltipHandle.
TooltipHandle<Payload>–
children
ReactNode | ({ payload }) => ReactNode–
PropTypStandard
delayÜberschreibt die Provider-Verzögerung für diesen Trigger.
number–
closeDelay
number–
closeOnClick
booleantrue
disabledVerhindert, dass dieser Trigger den Tooltip öffnet. Das Element bleibt aktiviert.
booleanfalse
handle
TooltipHandle<Payload>–
payload
Payload–
render
ReactElement | (props, state) => ReactElement<button>
AttributBeschreibung
data-slot="tooltip-trigger"Der Trigger.
data-popup-openVorhanden, solange sein Tooltip offen ist.
data-trigger-disabledVorhanden, wenn der Trigger den Tooltip nicht öffnen kann.
PropTypStandard
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
portalPropsEtwa container oder keepMounted.
TooltipPortal props–
AttributBeschreibung
data-slot="tooltip-content"Das Popup.
data-slot="tooltip-positioner"Der Positionierungs-Wrapper um das Popup.
data-sideDie Seite, auf der er nach dem Umklappen geöffnet wurde.
data-alignDie Ausrichtung.
data-instant"delay", "focus" oder "dismiss", wenn es ohne Animation öffnet oder schließt.
data-starting-styleVorhanden, während es einanimiert.
data-ending-styleVorhanden, während es ausanimiert.
--available-widthPlatz zwischen dem Trigger und dem Bildschirmrand.
--transform-originDer Punkt, von dem der Tooltip skaliert.

Wird von arrow für dich gerendert. Nutze es direkt nur, wenn du einen eigenen Content-Teil zusammensetzt.

AttributBeschreibung
data-slot="tooltip-arrow"Der Pfeil.
data-uncenteredVorhanden, wenn er nicht auf die Mitte des Triggers zeigen kann.

In Blocks verwendet

Blocks, die auf Tooltip aufbauen.