{
  "title": "Tooltip",
  "description": "Small floating label triggered by hover (web) or long-press (native). For short contextual hints — most often on icon-only buttons.",
  "url": "/docs/components/tooltip",
  "since": "0.0.6",
  "tags": [
    "overlay"
  ],
  "platform": "both",
  "source": "---\ntitle: Tooltip\ndescription: Small floating label triggered by hover (web) or long-press (native). For short contextual hints — most often on icon-only buttons.\nsince: 0.0.6\ntags: [overlay]\nplatform: both\ncategory: overlays\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Callout } from 'fumadocs-ui/components/callout';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"Tooltip\" />\n\n## At a glance\n\n- Compose: `Tooltip`, `TooltipTrigger`, `TooltipContent`. Trigger uses `asChild` by default — wrap any element (Button, Link, custom Pressable) and it becomes the activator.\n- Cross-platform: web opens on hover or focus; native opens on long-press (500ms hold).\n- Configurable open / close delays via `delayMs` (default 500ms) and `closeDelayMs` (default 0ms).\n- Accessibility: trigger gets `aria-describedby` pointing at the content's id (the tooltip _augments_ the trigger's accessible name — it does not replace it). Content gets `role=\"tooltip\"` with a unique id.\n\n<Callout type=\"info\" title=\"Tooltip vs Popover\">\n    - **Tooltip** — small hover/long-press hint with non-interactive text. Use for short labels like \"Click to view details\" on icon-only buttons. Augments the trigger's accessible name; not a focus stop.\n    - **Popover** — anchored, non-modal, can hold interactive content (forms, buttons, color pickers). Dismisses on outside click + Escape. Use Popover when the surface needs to be clicked into.\n</Callout>\n\n## Preview\n\n<Preview name=\"tooltip-basic\" />\n\n## Sides\n\n`TooltipContent` accepts a `side` of `'top'`, `'right'`, `'bottom'`, or `'left'` (default `'top'`). Pair it with `align` (`'start' | 'center' | 'end'`, default `'center'`) to fine-tune placement along the chosen edge.\n\n<Preview name=\"tooltip-sides\" />\n\n## Anatomy\n\n| Subcomponent | Role |\n|---|---|\n| `Tooltip` | Root — owns open state and the open / close timers. |\n| `TooltipTrigger` | Element that reveals the tooltip. `asChild` by default. |\n| `TooltipContent` | The floating label. Renders only while open. |\n\n## Delays\n\n```tsx\n// Default: 500ms before opening, 0ms before closing.\n<Tooltip>...</Tooltip>\n\n// Snappy hint — open after 200ms.\n<Tooltip delayMs={200}>...</Tooltip>\n\n// Forgiving close — give the cursor 150ms to come back.\n<Tooltip delayMs={300} closeDelayMs={150}>...</Tooltip>\n```\n\n## Open state — `open`, `defaultOpen`, `onOpenChange`\n\nPass `open` for controlled mode and pair with `onOpenChange`. Pass\n`defaultOpen` for uncontrolled mode when you want the tooltip\npre-opened on first render (rare, useful during onboarding tours).\nMixing the two prefers controlled and ignores `defaultOpen`.\n\n```tsx\nimport { useState } from 'react';\nimport { Button, Tooltip } from '@nori-ui/core';\n\n// Uncontrolled — Tooltip owns its open state.\nexport function Uncontrolled() {\n    return (\n        <Tooltip>\n            <Tooltip.Trigger>\n                <Button>Hover me</Button>\n            </Tooltip.Trigger>\n            <Tooltip.Content>Hint</Tooltip.Content>\n        </Tooltip>\n    );\n}\n\n// Controlled — you own the state, useful for forcing the tooltip open\n// during onboarding tours or syncing with a \"show all hints\" setting.\nexport function Controlled() {\n    const [open, setOpen] = useState(false);\n    return (\n        <Tooltip open={open} onOpenChange={setOpen}>\n            <Tooltip.Trigger>\n                <Button>Hover me</Button>\n            </Tooltip.Trigger>\n            <Tooltip.Content>Hint</Tooltip.Content>\n        </Tooltip>\n    );\n}\n```\n\n## Accessibility\n\nThe trigger receives `aria-describedby` referencing the content's id only while the tooltip is open. This means the tooltip text is announced _in addition to_ the trigger's existing accessible name — so an icon-only button still needs its own `aria-label`:\n\n```tsx\n<Tooltip>\n    <Tooltip.Trigger>\n        <Button leadingIcon={InfoIcon} aria-label=\"More info\" />\n    </Tooltip.Trigger>\n    <Tooltip.Content>Click to view details</Tooltip.Content>\n</Tooltip>\n```\n\nThe content has `role=\"tooltip\"` and is non-interactive (`pointerEvents: 'none'`) — if you need a panel users can click into, reach for `Popover` instead. Pressing Escape while the tooltip is open dismisses it.\n\n## Native behavior\n\nOn iOS and Android there is no hover, so the tooltip opens on a 500ms long-press of the trigger and dismisses on the next tap anywhere. The chip is rendered with absolute positioning relative to where it sits in the tree — the parent needs to allow overflow for the chip to peek out. For richer native overlays, prefer `Popover`.\n\n## Props\n\n### Tooltip\n\n<PropsTable component=\"Tooltip\" />\n\n### TooltipTrigger\n\n<PropsTable component=\"TooltipTrigger\" />\n\n### TooltipContent\n\n<PropsTable component=\"TooltipContent\" />\n"
}
