HoverCard
A hover-revealed floating surface that displays rich preview content when the pointer rests on an interactive trigger.
import { HoverCard } from "@glint/components/hover-card.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
trigger-box := Rectangle { width: 140px; height: 32px; border-radius: Tokens.radius-md; background: Tokens.color-secondary;
trigger := TouchArea { }
Text { text: "@glint-ui"; color: Tokens.color-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; vertical-alignment: center; } } } }
card := HoverCard { x: trigger-box.x; y: trigger-box.y; width: trigger-box.width; height: trigger-box.height; active: trigger.has-hover; content-width: 280px;
VerticalLayout { spacing: 8px;
Text { text: "Glint UI"; color: Tokens.color-foreground; font-weight: 600; font-size: Tokens.typography-body-size; }
Text { text: "Themable, accessible Slint component library built for desktop and embedded apps."; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; wrap: word-wrap; } } }}Usage
import { HoverCard } from "@glint/components/hover-card.slint";
export component AppWindow inherits Window { VerticalLayout { alignment: center;
badge := Rectangle { width: 120px; height: 32px; trigger := TouchArea { } Text { text: "@glint-ui"; } } }
// Give HoverCard the trigger's own geometry and bind active to hover: card := HoverCard { x: badge.x; y: badge.y; width: badge.width; height: badge.height; active: trigger.has-hover; content-width: 280px;
VerticalLayout { // preview card content } }}A HoverCard is an interactive floating surface that displays rich preview information
when a user hovers over a trigger element. Unlike Tooltip (which is for brief hints),
HoverCard can hold complex layouts and interactive elements.
Give the HoverCard element the trigger’s own position and dimensions (x, y, width, height),
and bind active to the trigger’s hover condition (trigger.has-hover).
Examples
Placement sides
side determines which edge of the trigger the card opens from: bottom (the default), top, left, or right.
import { HoverCard } from "@glint/components/hover-card.slint";import { PanelSide } from "@glint/components/panel.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 360px; background: Tokens.color-background;
VerticalLayout { alignment: center; spacing: 24px; padding: 32px;
HorizontalLayout { alignment: center; spacing: 24px;
top-box := Rectangle { width: 100px; height: 36px; border-radius: Tokens.radius-md; background: Tokens.color-secondary; top-area := TouchArea { } Text { text: "Top side"; color: Tokens.color-foreground; } }
bottom-box := Rectangle { width: 100px; height: 36px; border-radius: Tokens.radius-md; background: Tokens.color-secondary; bottom-area := TouchArea { } Text { text: "Bottom side"; color: Tokens.color-foreground; } } } }
top-card := HoverCard { x: top-box.x; y: top-box.y; width: top-box.width; height: top-box.height; active: top-area.has-hover; side: PanelSide.top; content-width: 220px;
Text { text: "Opens above the trigger with PanelSide.top."; color: Tokens.color-foreground; wrap: word-wrap; font-size: Tokens.typography-body-sm-size; } }
bottom-card := HoverCard { x: bottom-box.x; y: bottom-box.y; width: bottom-box.width; height: bottom-box.height; active: bottom-area.has-hover; side: PanelSide.bottom; content-width: 220px;
Text { text: "Opens below the trigger with PanelSide.bottom."; color: Tokens.color-foreground; wrap: word-wrap; font-size: Tokens.typography-body-sm-size; } }}Alignment
align specifies how the card aligns along the cross-axis of its placement side: start (default), center, or end.
import { HoverCard } from "@glint/components/hover-card.slint";import { PanelAlign } from "@glint/components/panel.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
trigger-box := Rectangle { width: 160px; height: 36px; border-radius: Tokens.radius-md; background: Tokens.color-secondary; trigger := TouchArea { } Text { text: "Centered preview"; color: Tokens.color-foreground; } } } }
card := HoverCard { x: trigger-box.x; y: trigger-box.y; width: trigger-box.width; height: trigger-box.height; active: trigger.has-hover; align: PanelAlign.center; content-width: 260px;
Text { text: "Aligned to center with PanelAlign.center."; color: Tokens.color-foreground; wrap: word-wrap; font-size: Tokens.typography-body-sm-size; } }}Hover delays
open-delay (default 300ms) prevents accidental opens during quick pointer passes. close-delay (default 300ms) provides ample time for the pointer to move across the gap from the trigger onto the hover card surface.
import { HoverCard } from "@glint/components/hover-card.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 560px; height: 320px; background: Tokens.color-background;
VerticalLayout { alignment: center; padding: 24px;
HorizontalLayout { alignment: center;
trigger-box := Rectangle { width: 160px; height: 36px; border-radius: Tokens.radius-md; background: Tokens.color-secondary; trigger := TouchArea { } Text { text: "Long close delay"; color: Tokens.color-foreground; } } } }
card := HoverCard { x: trigger-box.x; y: trigger-box.y; width: trigger-box.width; height: trigger-box.height; active: trigger.has-hover; open-delay: 200ms; close-delay: 500ms; content-width: 240px;
Text { text: "Generous 500ms close delay allows comfortable pointer movement."; color: Tokens.color-foreground; wrap: word-wrap; font-size: Tokens.typography-body-sm-size; } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
active | in bool | no default | The hover state that reveals the card — a TouchArea.has-hover, a focus flag, any condition the consumer computes. Glint never owns the trigger (ADR-0006). |
open-delay | in duration | 300ms | How long active must stay true before the card opens, so incidental hovers never flash it (Radix openDelay, which is 700ms — Glint opens sooner because 700ms reads as the card being broken rather than deliberate, and 300ms already outlasts a pointer crossing the trigger). |
close-delay | in duration | 300ms | Grace period after active drops before the card closes — long enough for the pointer to travel from the trigger onto the card (Radix closeDelay). |
content-width | in length | 280px | Width of the surface; its height follows the content. |
side | in PanelSide | PanelSide.bottom | Which edge of the trigger the card opens from — bottom by default, which is where it has always opened. The vocabulary is the Panel's (PanelSide.top, …) because the placement is: only the Panel can measure the surface an upward card has to sit on top of. |
align | in PanelAlign | PanelAlign.start | How the card lines up along the axis side leaves free. start is the trigger's leading edge, again the placement that was there before. |
is-open | out bool | no default | True while the card is on screen; mirrors PopupWindow.is-open. |
Enums
| Enum | Values |
|---|---|
PanelSide | top, right, bottom, left |
PanelAlign | start, center, end |
Accessibility
- Hover zone mechanics. The hover zone seamlessly unions the trigger’s bounding box, the gap, and the card surface so the pointer does not lose hover when travelling into the card.
- Pointer passthrough. While open, clicking the trigger dismisses the card and allows subsequent pointer clicks to reach the trigger control.
- Dismissable layer. Pressing Escape closes an open hover card and restores focus to whatever control held it.