Nasaq
Components

Tooltip

Short supplementary label shown on hover and keyboard focus of a trigger. Shorthand plus composable parts over Base UI Tooltip.

PreviewOpen ↗

Code

import { Button, Tooltip } from "@fadymondy/nasaq/web";import { Settings } from "lucide-react";export function SettingsButton() {  return (    <Tooltip content="Settings">      <Button size="icon" variant="ghost" aria-label="Settings">        <Settings />      </Button>    </Tooltip>  );}

Overlays · stable

Live examples and controls: Tooltip in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/tooltip.json

A small inverted label that appears when the user hovers or focuses a trigger. Tooltip is the shorthand (content + one child); TooltipProvider, TooltipRoot, TooltipTrigger and TooltipContent are the parts when you need control.

Tooltips are supplementary. They do not appear on touch, so never put essential information in one.

When to use

  • Naming an icon-only button (in addition to its aria-label).
  • A keyboard shortcut hint or a short clarification.

When not to use

  • Essential information, errors or instructions: put it in the page or a field description.
  • Interactive content (links, buttons): use a popover or Dialog.
  • Long text: max width is max-w-64.

Import

import { Tooltip, TooltipProvider } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"

Quick start

import { Button, Tooltip } from "@fadymondy/nasaq/web";
import { Settings } from "lucide-react";

export function SettingsButton() {
  return (
    <Tooltip content="Settings">
      <Button size="icon" variant="ghost" aria-label="Settings">
        <Settings />
      </Button>
    </Tooltip>
  );
}

Anatomy

Tooltip (shorthand)
├─ TooltipRoot                 Base UI Tooltip.Root
│  ├─ TooltipTrigger           Base UI Tooltip.Trigger, render={children}
│  └─ TooltipContent           Portal + positioner + popup   data-slot="tooltip-content"
TooltipProvider                Base UI Tooltip.Provider (optional; shares delay across tooltips)

API

Tooltip

PropTypeDefaultDescription
contentReactNoderequiredThe tooltip text.
childrenReactElementrequiredThe trigger element. It is passed to render, so it must be a single element that accepts a ref and props and is focusable.
side?`"top""bottom""left"
align?`"start""center""end"`
sideOffset?number6Gap to the trigger in px.
delay?numberBase UI default, or the TooltipProvider delayHover delay before opening, passed to Tooltip.Trigger.
open?booleanuncontrolledControlled open state (Base UI Tooltip.Root).
onOpenChange?(open, details) => voidnoneCalled when the tooltip opens or closes.

TooltipProvider, TooltipRoot, TooltipTrigger

Aliases of Base UI Tooltip.Provider, Tooltip.Root, Tooltip.Trigger.

ExportCommon props
TooltipProviderdelay?, closeDelay?, timeout?: shared open delay for a group of tooltips.
TooltipRootopen?, defaultOpen?, onOpenChange?, disabled?
TooltipTriggerrender?, plus native attributes.

TooltipContent

TooltipContentProps extends Base UI Tooltip.Popup props.

PropTypeDefaultDescription
side?as Tooltip.Positioner side"top"Preferred side. Prefer inline-start / inline-end.
align?as Tooltip.Positioner alignBase UI default ("center")Alignment along the trigger edge.
sideOffset?number6Gap to the trigger in px.
className?stringnoneMerged onto the popup.
children?ReactNodenoneTooltip content.

Examples

Placement

import { Button, Tooltip } from "@fadymondy/nasaq/web";

export function Placement() {
  return (
    <div className="flex gap-3 p-10">
      <Tooltip content="Shown below" side="bottom">
        <Button>Hover me</Button>
      </Tooltip>
      <Tooltip content="Beside, at the inline end" side="inline-end">
        <Button>Inline end</Button>
      </Tooltip>
    </div>
  );
}

Shared delay with a provider, Arabic

import { Button, Tooltip, TooltipProvider } from "@fadymondy/nasaq/web";
import { Pencil, Trash2 } from "lucide-react";

export function Toolbar() {
  return (
    <TooltipProvider delay={300}>
      <div className="flex gap-1">
        <Tooltip content="تعديل">
          <Button size="icon-sm" variant="ghost" aria-label="تعديل">
            <Pencil />
          </Button>
        </Tooltip>
        <Tooltip content="حذف">
          <Button size="icon-sm" variant="ghost" aria-label="حذف">
            <Trash2 />
          </Button>
        </Tooltip>
      </div>
    </TooltipProvider>
  );
}

Composed parts

import { Button, TooltipContent, TooltipRoot, TooltipTrigger } from "@fadymondy/nasaq/web";

export function Composed() {
  return (
    <TooltipRoot>
      <TooltipTrigger render={<Button variant="ghost" />}>Info</TooltipTrigger>
      <TooltipContent side="bottom" sideOffset={10}>
        Extra detail
      </TooltipContent>
    </TooltipRoot>
  );
}

Accessibility

Base UI opens the tooltip on pointer hover and on keyboard focus of the trigger, and closes it on blur, pointer leave and Esc.

KeyAction
TabFocusing the trigger opens the tooltip.
EscCloses the tooltip.
  • The tooltip is not a substitute for a label. An icon-only trigger still needs its own aria-label.
  • Tooltips do not open on touch devices.
  • Localise content.

RTL & i18n

  • Use side="inline-start" / "inline-end" for horizontal placement so it mirrors; "left" / "right" are still accepted but physical, so they do not flip. Prefer the logical sides.
  • Text follows the document dir. No built-in strings.

Styling & tokens

  • Inverted surface: bg-foreground, text-background, rounded-control, text-caption, max-w-64.
  • Motion: 150ms opacity via data-starting-style / data-ending-style.
  • Target [data-slot=tooltip-content].

Do / Don't

  • Do keep tooltips to a few words.
  • Do pair an icon-only button's tooltip with the same aria-label.
  • Don't put essential or interactive content in a tooltip.
  • Don't attach a tooltip to a non-focusable element.

Lab

https://docs.nasaqui.com/?path=/docs/components-overlays-tooltip--docs

On this page