Skip to content
Component installs need the registry setup
VegaStack Design

Hover Card

A preview surface for sighted users — a link-shaped trigger, configurable open and close delays, and side/align positioning.

Status
stable
Since
0.1.0
Accessibility pattern
sighted-preview only; the trigger carries the content

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Hover Card before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify hover-card

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/hover-card

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

Usage

import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card";

<HoverCard>
  <HoverCardTrigger>Hover</HoverCardTrigger>
  <HoverCardContent>
    The React Framework – created and maintained by @vercel.
  </HoverCardContent>
</HoverCard>;

Anatomy

HoverCard — data-slot="hover-card"
HoverCardTrigger — data-slot="hover-card-trigger"
HoverCardContent — data-slot="hover-card-content" | "hover-card-portal"

Examples

Composition

HoverCardContent composes the portal, the positioner and the popup, so a hover card is two parts at the call site.

HoverCard
├── HoverCardTrigger
└── HoverCardContent

Trigger Delays

delay and closeDelay on HoverCardTrigger set how long the pointer must rest before the card opens, and how long it lingers after the pointer leaves. Base UI's defaults are 600ms and 300ms; the close delay is what lets the pointer travel from the trigger into the card without dismissing it.

Positioning

side and align on HoverCardContent choose the edge the card opens from and where along that edge it sits; sideOffset and alignOffset move it from there. Collision avoidance flips the card when the requested side has no room, and the resolved placement is readable as data-side and data-align.

Basic

A profile preview: handle, summary, and a muted footer line. The card is decoration over content the trigger already links to, so nothing inside it is the only route to that information.

Sides

All four physical sides, each with the same card.

RTL

Wrap the subtree in DirectionProvider direction="rtl" so Base UI resolves the logical sides. side="inline-start" and side="inline-end" swap with the reading direction; left, right, top and bottom stay where they are named. Pass dir to HoverCardContent as well — the card portals out of the dir subtree and would otherwise render its text left-to-right.

API Reference

HoverCardContent adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

HoverCard and HoverCardTrigger add no props of their own — each accepts everything the Base UI part accepts, including the trigger's delay and closeDelay.

Accessibility

  • Renders Base UI's Preview Card. The trigger is a link, the card opens on hover and on keyboard focus, and Esc closes it.
  • A hover card is an enhancement for sighted pointer users, not a delivery mechanism. Never put content in one that exists nowhere else, and never put a control in one that has no equivalent outside it.
  • The card portals out of the trigger's subtree, so HoverCardContent re-applies the theme scope on the positioner inside the portal (OVL-13) — a dark or scoped surface survives the portal.
  • Focus is the global 2px :focus-visible outline from base.css; there is no ring glow on the trigger or on the card.
  • Keep closeDelay non-zero so a pointer can cross the gap between the trigger and the card.
ContractStates tested
Behaviourdefault, open, closed
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test, keyboard-navigation
Visualdefault, open, closed

Do / Don't

Do
Use a HoverCard to preview something a link already leads to — a profile, a product, a page.
Don't
Put actions or unique content inside a HoverCard; a touch or keyboard-only user may never open it.

Deviations

Upstream's file plus packages/ui/upstream/patches/hover-card.patch. Every hunk:

  • OVL-13 — HoverCardContent reads useInternalThemeScope() and attaches it to the Positioner INSIDE the portal. tooling/verify-portal-theme-scope.mjs inventories this host.
  • BRD-1 — HoverCardContent draws a real 1px border border-border instead of upstream's ring-1 ring-foreground/10 box-shadow outline. A border survives forced colours and matches Elevation; nothing else about the surface moves.
  • DOC-2 — import { cn } from "cn" becomes @vegastack/design; our npm layer owns cn.
  • DOC-1 — prettier reflow plus the registry-stamp.mjs provenance header the three-copy model needs; no decision of its own.

On this page