HoverCard
A rich preview panel on Ark UI's HoverCard — delayed, portaled and animated. Hover a trigger, see a card; add HoverCard.Group so a row of triggers shares one card that slides between them.
HoverCard sits between Popover and Tooltip: it's a rich panel and it opens on a delay — the "hover a username, see a profile card" pattern. It's a styled layer over Ark UI's HoverCard: Ark owns the open/close state, the open/close delays, positioning, and dismiss-on-outside-interaction; the kit adds the portaled, animated panel chrome (reusing Popover's surface).
Unlike Tooltip, there's no single-call convenience form — a hover card's content is arbitrary rich markup, so it's compound-only, like Popover.
import { HoverCard } from '@42-central/ui-react/hover-card';
<HoverCard.Root>
<HoverCard.Trigger asChild>
<a href="/norm">@the-norm</a>
</HoverCard.Trigger>
<HoverCard.Content>
Rich <strong>preview</strong> content.
</HoverCard.Content>
</HoverCard.Root>Playground
HoverCard
Dir
Props
Code
<HoverCard
position="bottom"
openDelay={600}
closeDelay={300}
width="auto"
/>Basic usage
Code
<HoverCard.Root>
<HoverCard.Trigger asChild>
<a href="/norm">@the-norm</a>
</HoverCard.Trigger>
<HoverCard.Content className="flex w-64 flex-col gap-2 p-3">
<div className="flex items-center gap-3">
<Avatar name="The Norm" color="name" />
<div className="flex flex-col">
<Text size="sm" span className="font-medium">The Norm</Text>
<Text size="xs" c="muted" span>@the-norm</Text>
</div>
</div>
<Text size="sm">
The reference every project is graded against.
</Text>
</HoverCard.Content>
</HoverCard.Root>Delays
Ark's machine defaults to a 600ms openDelay and a 300ms closeDelay — long enough that an incidental pass of the cursor doesn't pop a card open, but short enough to feel responsive on purpose. Both pass straight through to Ark; the kit adds no timer logic of its own.
Code
<HoverCard.Root openDelay={0} closeDelay={0}>…</HoverCard.Root>Positioning
position sets the placement; the panel flips and shifts to stay in view (Ark / Floating UI). Prefer the logical, RTL-aware sides start / end — they resolve to left / right based on the active direction, so the card mirrors automatically under dir="rtl". (Physical left / right and Ark's aligned variants like top-start still work.) Unlike Popover, width defaults to "auto" (size to content) rather than "target" — a hover card's content is usually much larger than its trigger.
Code
<HoverCard.Root position="start">…</HoverCard.Root>
<HoverCard.Root position="top">…</HoverCard.Root>
<HoverCard.Root position="bottom">…</HoverCard.Root>
<HoverCard.Root position="end">…</HoverCard.Root>Portal
withinPortal (default true) renders the card in a Portal so it escapes clipping ancestors; pass container to portal somewhere other than document.body. Set withinPortal={false} to keep the card inline instead — it's then clipped by an overflow: hidden ancestor like any other child, as long as that ancestor (or one between it and the panel) is positioned (e.g. position: relative) — standard CSS: overflow: hidden alone doesn't clip a position: absolute descendant unless something establishes a containing block for it.
A clipping ancestor (overflow-hidden)
Code
<HoverCard.Root>…</HoverCard.Root> // portaled by default
<HoverCard.Root withinPortal={false}>…</HoverCard.Root> // clips with its containerDisabled
disabled prevents the card from opening at all — hover and focus are both no-ops.
Code
<HoverCard.Root disabled>…</HoverCard.Root>Grouping — HoverCard.Group
HoverCard.Group puts several trigger targets (a row of avatars, say) on one shared card instead of a separate card per trigger — the same HoverCard.Trigger / HoverCard.Content parts HoverCard.Root uses, just several of each, matched by a shared value (like Tabs.Trigger / Tabs.Content). Ark's HoverCard machine natively supports several named triggers under a single Root/Content — hovering from one target to the next while the card is already open just repositions the same panel, no close/reopen and no delay replay — and a CSS transition on that reposition makes the panel visibly slide from one target to the next.
Trigger/Content must be Group's own direct children — Group renders no DOM node of its own (like Root), so for row-level layout (overlapping avatars, say), wrap Group itself instead of wrapping them inside it.
Code
<HoverCard.Group>
<HoverCard.Trigger value="ada">
<Avatar name="Ada Lovelace" />
</HoverCard.Trigger>
<HoverCard.Trigger value="grace">
<Avatar name="Grace Hopper" />
</HoverCard.Trigger>
<HoverCard.Content value="ada">Ada's card content</HoverCard.Content>
<HoverCard.Content value="grace">Grace's card content</HoverCard.Content>
</HoverCard.Group>Accessibility
A hover card is a preview of where a link goes, for sighted mouse users. That is Ark's design, and the kit keeps it:
- Touch never opens it. Ark ignores touch pointers, because a tap belongs to the link it previews.
- Its content can't be focused. Focusing the trigger opens the card, but Tab moves past it, so nothing inside is keyboard-reachable.
- Screen readers aren't told it opened. The trigger carries no
aria-expandedoraria-controls.
So keep its content a preview — everything in it should also be reachable by following the trigger's link. For a card people act on (a copy button, a "View profile" link, a follow button), use Popover's mode="hover" instead: it keeps the delayed hover preview, and a click, tap or Enter pins it as a regular, focusable, dismissable popover.
Anatomy
HoverCard.Root— wraps Ark'sHoverCard.Root; owns open state, delays, and positioning.HoverCard.Trigger— the trigger; useasChildto wrap your own element. Inside aGroup, give it avalueto identify its target.HoverCard.Content— the portaled, animated panel. Inside aGroup, give it avaluematching itsTrigger.HoverCard.Group— one sharedRoot/Contentacross severalvalue-matchedTrigger/Contentpairs, so the panel slides between them instead of closing and reopening.
testId
HoverCard.Root and HoverCard.Group are headless providers with no DOM node of their own, so neither takes a testId. HoverCard.Content (the panel — one testId per value, inside a Group) takes its own testId. HoverCard.Trigger is a raw Ark re-export and was left alone: Ark already forwards an arbitrary data-testid straight through natively. See Testing.
API
HoverCard.Root props (beyond Ark's HoverCard.Root):
Prop
Type
HoverCard.Group props (the same panel-level shape as HoverCard.Root, since there's one shared panel):
Prop
Type
HoverCard.Content's one prop beyond Ark's own HoverCard.Content, meaningful only inside a Group:
Prop
Type
Changelog
Tooltip
A hover/focus hint on Ark UI's Tooltip — ARIA-wired (role="tooltip" + aria-describedby), portaled and animated. Drops into running text or wraps any element.
Modal
Centered overlay dialog on Ark UI's Dialog machine — focus trap, scroll lock, RTL and animated enter/exit, with a flat props API and composable parts.