Askr
UI & Components

Tooltip

Use tooltip with the current published Askr packages.

  • @askrjs/ui/tooltip0.0.13
  • @askrjs/themes/tooltip0.0.13

How to use tooltip

Tooltip should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.

  1. Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
  2. Keep tooltip labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
  3. Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { Button, Tooltip, TooltipContent, TooltipTrigger } from '@askrjs/themes/components';

<Tooltip>
  <TooltipTrigger asChild><Button aria-label="Archive project">Archive</Button></TooltipTrigger>
  <TooltipContent>Move this project to the archive</TooltipContent>
</Tooltip>

Purpose

Tooltip shows a short hint tied to an element, opened by hover or keyboard focus rather than a click. It's the right choice for a one-line label or clarification an icon button needs; once the content grows into something a user has to read or interact with, reach for Popover or Hover Card instead. Because it opens on focus, it also doubles as an accessible-name fallback for icon-only triggers.

Install and import

The headless behavior lives at `@askrjs/ui/tooltip`, exporting `Tooltip`, `TooltipTrigger`, `TooltipPortal`, and `TooltipContent`. Most apps won't import from there directly — pull the styled version from `@askrjs/themes/tooltip`, which wraps the same primitives with the default theme's positioning and animation CSS already applied.

Live examples

A minimal tooltip is a `Tooltip` wrapping a `TooltipTrigger` and a `TooltipContent` inside `TooltipPortal`, with the trigger set to whatever element the hint describes — commonly an icon `Button`. Toggling `side` and `align` on `TooltipContent` is the fastest way to see how the positioning engine reacts to different anchor placements before wiring up real content.

Anatomy

`Tooltip` is the state root; `TooltipTrigger` is the hover/focus target and takes the same props as a button-like element. `TooltipPortal` renders `TooltipContent` outside the normal DOM flow so it isn't clipped by an ancestor's `overflow: hidden`, and `TooltipContent` is the actual bubble, positioned relative to the trigger.

State model

Openness is a plain boolean on the root: pass `open` and `onOpenChange` to drive it from outside, or let `defaultOpen` seed uncontrolled behavior and manage it internally. There's no separate state for hover versus focus — both funnel into the same `open` value, so you don't need to reconcile two code paths for pointer and keyboard users.

Keyboard and accessibility

The content renders with `role="tooltip"` and the trigger gets `aria-describedby` pointing at it, per `TOOLTIP_A11Y_CONTRACT` — so screen readers announce the hint as a description of the trigger, not as a separate focusable region. Focusing the trigger with the keyboard opens it the same as hovering does, and moving focus away closes it; there's no dedicated close button or additional tab stop to manage.

Styling and tokens

`TooltipContent` picks up `data-state`, `data-side`, `data-align`, and `data-disabled` attributes, which is what the default theme's CSS hooks into for entrance/exit transitions and directional arrow placement. Stacking is controlled by the shared overlay z-index scale, where tooltips sit above toasts at `var(--ak-z-tooltip, 1600)` so they're never hidden behind other overlay layers.

API

`TooltipContent` accepts `forceMount`, `side`, `align`, and `sideOffset` on top of standard div props — `forceMount` keeps it in the DOM even when closed, which matters if you're animating exit with CSS rather than unmounting. `TooltipTrigger` takes the same prop surface as any button-like Askr primitive, including an `asChild`-style variant for rendering a custom trigger element.

Edge cases

Tooltips don't fire from a touch tap the way hover-based UI generally doesn't on touch devices, so don't rely on tooltip content to convey anything essential — treat it as supplementary. If the trigger is disabled, `data-disabled` is still set for styling, but you're responsible for deciding whether a disabled control should still explain itself via tooltip. Rapid focus changes between adjacent triggers can also cause overlapping open/close transitions if your CSS doesn't account for the interrupted `data-state`.

See Popover for click-triggered, persistently-open content, and Hover Card for richer hover-triggered previews that can contain interactive elements. Dropdown and Menu cover the click-to-open, keyboard-navigable list pattern that Tooltip intentionally doesn't attempt.