Askr documentation
UI & Components

Tooltip

Tooltip: anatomy, keyboard behavior, state, and theming in Askr.

Example

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>
Try Tooltip

Reveal supporting text from an accessible trigger.

Published props

Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.

TooltipContentAsChildProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
alignalign?: OverlayAlign | undefined;——
asChildasChild: true;——
childrenchildren: JSXElement;——
forceMountforceMount?: boolean | undefined;——
refref?: Ref<Element>;——
sideside?: OverlaySide | undefined;——
sideOffsetsideOffset?: number | undefined;——

TooltipContentOwnProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
alignalign?: OverlayAlign | undefined;——
forceMountforceMount?: boolean | undefined;——
sideside?: OverlaySide | undefined;——
sideOffsetsideOffset?: number | undefined;——

TooltipContentProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
alignalign?: OverlayAlign | undefined;——
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
forceMountforceMount?: boolean | undefined;——
refref?: Ref<HTMLDivElement>;——
sideside?: OverlaySide | undefined;——
sideOffsetsideOffset?: number | undefined;——

TooltipOwnProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
childrenchildren?: unknown;——
defaultOpendefaultOpen?: boolean | undefined;——
idid?: string | undefined;——
onOpenChangeonOpenChange?: ((open: boolean) => void) | undefined;——
openopen?: boolean | undefined;——

TooltipPortalProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
childrenchildren?: unknown;——

TooltipProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
childrenchildren?: unknown;——
defaultOpendefaultOpen?: boolean | undefined;——
idid?: string | undefined;——
onOpenChangeonOpenChange?: ((open: boolean) => void) | undefined;——
openopen?: boolean | undefined;——

TooltipTriggerAsChildProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
asChildasChild: true;——
childrenchildren: JSXElement;——
disableddisabled?: boolean | undefined;——
onPressonPress?: ((event: PressEvent) => void) | undefined;——
refref?: Ref<Element>;——
typetype?: undefined;——

TooltipTriggerProps

Import from @askrjs/themes/tooltip.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
disableddisabled?: boolean | undefined;——
onPressonPress?: ((event: PressEvent) => void) | undefined;——
refref?: Ref<HTMLButtonElement>;——
typetype?: "button" | "submit" | "reset" | undefined;——

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

Tooltip content exposes role="tooltip", and the trigger references it through aria-describedby. Verify opening from focus and hover, closing on blur or pointer exit, and cleanup when the trigger is removed.

Styling and tokens

TooltipContent picks up data-state, data-side, and data-align attributes, which is what the default theme's CSS hooks into for entrance/exit transitions and directional arrow placement — data-disabled is set on TooltipTrigger instead, reflecting the trigger's disabled state, not the content. Stacking is controlled by the shared overlay z-index scale, where tooltips sit above toasts at var(--ak-z-tooltip) (1600, no CSS fallback value — the token is expected to always be defined by the loaded theme) 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.