Tooltip
A hover-revealed bubble that displays a brief description or keyboard shortcut when a trigger receives pointer hover or keyboard focus.
import { Button } from "@glint/components/button.slint";import { Tooltip } from "@glint/components/tooltip.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 480px; height: 180px; background: Tokens.color-background;
HorizontalLayout { alignment: center; padding: 48px;
Tooltip { text: "Add to favorites";
Button { text: "Bookmark"; } } }}Usage
import { Button } from "@glint/components/button.slint";import { Tooltip, TooltipSide } from "@glint/components/tooltip.slint";
export component AppWindow inherits Window { HorizontalLayout { alignment: center;
Tooltip { text: "Save document"; side: TooltipSide.top;
Button { text: "Save"; } } }}A Tooltip wraps an interactive trigger control via its @children slot and reveals
a concise contextual label after a brief hover delay.
Unlike standard toolkit tooltips, Glint’s Tooltip renders in a floating PopupWindow layer
above other elements, sizes itself to its text using design tokens, and supports dual pointer/keyboard
activation.
Examples
Sides
side sets which edge of the trigger the tooltip bubble anchors to: top (the default), right, bottom, or left.
import { Button } from "@glint/components/button.slint";import { Tooltip, TooltipSide } from "@glint/components/tooltip.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 240px; background: Tokens.color-background;
VerticalLayout { alignment: center; spacing: 24px; padding: 32px;
HorizontalLayout { alignment: center; spacing: 16px;
Tooltip { text: "Top tooltip"; side: TooltipSide.top; Button { text: "Top"; } }
Tooltip { text: "Right tooltip"; side: TooltipSide.right; Button { text: "Right"; } }
Tooltip { text: "Bottom tooltip"; side: TooltipSide.bottom; Button { text: "Bottom"; } }
Tooltip { text: "Left tooltip"; side: TooltipSide.left; Button { text: "Left"; } } } }}Keyboard focus activation
active lets you trigger the tooltip from keyboard focus. By binding active to the wrapped control’s focus-held property, keyboard users navigating with Tab receive the same tooltip hints as pointer users.
import { Button } from "@glint/components/button.slint";import { Tooltip } from "@glint/components/tooltip.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 480px; height: 180px; background: Tokens.color-background;
HorizontalLayout { alignment: center; padding: 48px;
Tooltip { text: "Press Enter or Space to commit changes"; active: save-btn.focus-held;
save-btn := Button { text: "Save changes"; } } }}Custom hover delays
open-delay (default 300ms) controls how long the pointer must rest on the trigger before the bubble opens, preventing visual noise during rapid pointer transit. close-delay (default 100ms) provides a brief grace period to absorb pointer jitter.
import { Button } from "@glint/components/button.slint";import { Tooltip } from "@glint/components/tooltip.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 480px; height: 180px; background: Tokens.color-background;
HorizontalLayout { alignment: center; padding: 48px;
Tooltip { text: "Opens swiftly"; open-delay: 150ms; close-delay: 200ms;
Button { text: "Hover me"; } } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
text | in string | no default | Label rendered inside the bubble. |
side | in TooltipSide | TooltipSide.top | Which edge of the trigger to anchor to — top (default), right, bottom, left. |
open-delay | in duration | 300ms | How long the pointer must rest on the trigger before the bubble opens, so incidental hovers never flash it. Radix's delayDuration is 700ms; Glint opens sooner, and matches HoverCard.open-delay — two surfaces that both appear on hover must not appear at different speeds, or the slower one reads as broken. |
close-delay | in duration | 100ms | How long the bubble outlives a pointer leaving it. Short, because the delay is only there to absorb jitter at the zone edge — long enough and the bubble feels sticky. Published because HoverCard publishes it and the two were the same machine with one of them spelling this into a timer. |
active | in bool | no default | A second reason to show the bubble, ORed with the pointer's. shadcn's tooltip is defined as opening on keyboard focus *or* hover, so a keyboard user gets what a mouse user gets; bind this to the wrapped control's focus and it does: Tooltip { text: "Save and continue"; active: save.focus-held; save := Button { text: "Save"; } } focus-held rather than focus-visible, and the difference is the whole of why this property is hard: showing the bubble opens a PopupWindow, which takes the window's focus, which clears focus-visible on the control below — so a bubble gated on it closed itself the instant it appeared, the focus came back, and it opened again. Measured at 400ms a cycle, with the control's ring blinking in antiphase. focus-held is the same focus with the popup's borrowing discounted, so the bubble stops answering a question its own existence makes unanswerable. Tooltip cannot read it off the wrapped control itself: Slint reports focus only to the element that holds it, never to an ancestor, so a component that owns its trigger as @children still cannot see the trigger's focus. HoverCard.active is the same property for the same reason, and the two hover surfaces now agree. Which is why the controls that own a keyboard focus scope publish focus-visible themselves — Button, Toggle, Checkbox, Switch, Slider and Select. Wrapping one in a second FocusVisibleScope does not work and is not the shape to reach for: the wrapper reports its *own* focus, not the control's, and it adds a tab stop of its own that lands ahead of the control and answers nothing. One limit stays, and it is the pointer's limit on the keyboard's axis: an open PopupWindow takes the window's focus as well as its pointer events, so once the bubble is up the control below no longer holds the keyboard. The control answers Enter for the whole open-delay before that, and the bubble is a hint rather than a step in the task, so this is the same trade the zone below already documents. It has a second face now that focus-held keeps the bubble up: Tab cannot reach the control to report that the focus left, so a bubble the keyboard raised stays until the consumer clears active or a click dismisses the popup. That is the trade against a bubble that flashes twice a second, which is what gating on focus-visible costs. |
is-open | out bool | no default | Whether the bubble is currently shown; mirrors PopupWindow.is-open rather than being set by hand, so it cannot disagree with the popup. |
Enums
| Enum | Values |
|---|---|
TooltipSide | top, right, bottom, left |
Accessibility
- Dual activation. Tooltips support pointer hover and keyboard focus simultaneously
when
activeis bound to the trigger’sfocus-heldproperty. - Hover zone & pointer routing. The popup creates a combined hover zone spanning both the trigger and the bubble, ensuring the bubble does not flicker. A click in that zone dismisses the tooltip and is consumed; the wrapped control responds to the next click.
- Supported dismissal. A pointer-opened tooltip closes after the pointer
leaves the trigger and hover zone, or immediately when that zone is clicked.
Tooltipdoes not handle Escape itself. - Concise descriptions. Tooltip text should be reserved for brief hints or shortcuts;
for rich or multi-line content, use
HoverCardorPopoverinstead.