Hover Card
For sighted users to preview content available behind a link.
The framework for shipping fast websites. Built in Rust.
HoverCard is a floating card revealed on hover, for rich content (mini profile, link preview, contextual details). It is similar to Tooltip but wider (w-64 by default) and can hold images and links.
CSS-only pattern: the trigger and the content sit in a wrapper that carries the named group class group/hovercard (named so that it does not collide with another hover group higher up). The content goes from invisible opacity-0 to visible opacity-100 via group-hover/hovercard:, so the hover reveal needs no JavaScript. The ui.js island adds the keyboard: the card also opens when focus enters the HoverCard, and closes on blur and on Escape.
Use it for: a user mini profile when hovering an @username, a preview of an external link, a summary of a listed entity.
Installation
$ bext ui add hover-card
Copy and paste the following into src/components/ui/hover-card.tsx.
This component is interactive. Make sure the shared island is loaded (see Islands). It reacts to data-ui="hover-card" hooks.
Usage
Anatomy
<HoverCard> // group/hovercard
├── <HoverCardTrigger>
│ └── <a href="/user/@bext">@bext</a>
└── <HoverCardContent align="center"> // invisible → visible on hover or focus
└── ...rich content...API Reference
HoverCard
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Extra classes on the wrapper, a with relative inline-block and the group/hovercard class. |
HoverCardTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Element to hover or focus to reveal the card. When it contains no link or button, the island makes the trigger itself a tab stop. |
HoverCardContent
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "start" | "center" | "end" | "center" | Horizontal alignment. start=left-0, center=left-1/2 -translate-x-1/2, end=right-0. |
| id | string | — | Optional id on the card, for example to reference it with aria-describedby. |
| className | string | — | Default: absolute top-full mt-2 w-64 rounded-md border bg-popover p-4 shadow-md. |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-slot | every sub-component | "hover-card" | "hover-card-trigger" | "hover-card-content" | Stable selector for custom styling, and the hooks the island listens on. |
| data-state | HoverCard | "open" | "closed" | Set by the island when the card is open by hover or by focus. |
Keyboard
| Key | Action |
|---|---|
| Tab | Focusing the trigger reveals the card. Tab then moves through the links inside the card; the card closes when focus leaves the HoverCard. |
| Escape | Hides the card. Focus stays on the trigger. The card shows again on the next focus or hover. |
Accessibility
The card opens on hover and on keyboard focus, stays open while the pointer or the focus is inside it, and closes on blur, on pointer leave and on Escape. Prefer a link or a <button> inside HoverCardTrigger; when the trigger holds plain text, the island gives it tabindex="0" so it can still be focused.
The card has no role and is not linked to the trigger with ARIA: a screen reader reaches its content in reading order, after the trigger. It cannot be opened on a touch screen without a hover. For content the user must be able to open on demand, use Popover.
Use it only for non-essential content: the information must remain reachable without hover (e.g. the username stays clickable even without seeing the card).
Examples
Aligned start
align="start": flush with the left edge of the trigger.
Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom: fine-grained reactivity, no virtual DOM.
On This Page