Hover Card
Overview
Displays a richer preview card when the trigger is hovered or receives focus. SHoverCard combines the Aria HoverCardRoot/HoverCardTrigger/HoverCardPositioner/HoverCardPopup/HoverCardArrow primitive family (built on the shared Popper) with the hoverCardVariants style recipe (3 slots, 6 sizes).
Use a hover card for a non-blocking, hover-triggered preview (user profiles, repository previews, inline metadata). For a small text hint use tooltip; for click-triggered rich content use popover.
Usage
Features
- 🧩 Aria/styled split —
HoverCardCompactaggregates the popper trigger, positioner, popup and arrow;SHoverCardonly injects styles and forwards slots/events - ⏱️ Open/close delay —
openDelay(default 700ms) /closeDelay(default 300ms) tune the hover latency - 🎯 Placement — full popper
placementcontrol with collision avoidance and side-aware slide animations - 🔽 Arrow —
showArrowrenders a positioned arrow; configurable viaarrowProps - ⌨️ Focus trigger — also opens on keyboard focus, not just pointer hover
- 📐 6 sizes — xs–2xl
size; per-slotuioverrides - ♿ Accessible — dismiss on Escape/outside interaction,
axe-corezero violations open and closed
Component family
SHoverCard(styled) — the entry wrapper;hoverCardVariantsrecipe with dynamic slot forwardingHoverCardRoot(Aria) — the state owner;openviauseControllableState,openDelay/closeDelay, provides the popper rootHoverCardTrigger(Aria) — the anchor that opens the card on hover/focusHoverCardPositioner/HoverCardPositionerImpl(Aria) — the dismissable, positioned surface (built onPopperPositioner)HoverCardPopup(Aria) — the popup bodyHoverCardArrow(Aria) — the popper arrowHoverCardCompact(Aria) — the aggregated composite; composes trigger/positioner/popup/arrow and exposes the slots
Demo
Basic
Custom Styling
API
Aria Layer
8HoverCard
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | class of popup |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<HoverCardUi> | — | Per-slot class overrides for the component. |
placement | import("@/index").Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopperAnchorProps | — | Properties forwarded to the trigger element. |
portalProps | HoverCardPortalProps | — | Properties forwarded to the portal element. |
positionerProps | HoverCardPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | HoverCardPopupProps | — | Properties forwarded to the popup element. |
arrowProps | HoverCardArrowProps | — | Properties forwarded to the arrow element. |
defaultOpen | boolean | — | The open state of the hover card when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the hover card. |
openDelay | number | — | The duration from when the pointer enters the trigger until the hover card opens. |
closeDelay | number | — | The duration from when the pointer leaves the trigger or popup until the hover card closes. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | — |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the hover card changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
trigger | — | Custom content for the trigger slot. |
HoverCardArrow
HoverCardCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement | import("@/index").Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopperAnchorProps | — | Properties forwarded to the trigger element. |
portalProps | HoverCardPortalProps | — | Properties forwarded to the portal element. |
positionerProps | HoverCardPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | HoverCardPopupProps | — | Properties forwarded to the popup element. |
arrowProps | HoverCardArrowProps | — | Properties forwarded to the arrow element. |
defaultOpen | boolean | — | The open state of the hover card when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the hover card. |
openDelay | number | — | The duration from when the pointer enters the trigger until the hover card opens. |
closeDelay | number | — | The duration from when the pointer leaves the trigger or popup until the hover card closes. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | — |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the hover card changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
trigger | — | Custom content for the trigger slot. |
HoverCardPopup
HoverCardPortal
HoverCardPositioner
Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement | Placement | undefined | The placement of the floating element.
If used, it will override the `side` and `align` props. |
side | Side | 'bottom' | The preferred side of the trigger to render against when open. Will be reversed when collisions occur and
avoidCollisions is enabled. |
sideOffset | number | 0 | The distance in pixels from the trigger. |
sideFlip | boolean | true | Flip to the opposite side when colliding with boundary. |
align | Align | 'center' | The preferred alignment against the trigger. May change when collisions occur. |
alignOffset | number | 0 | An offset in pixels from the `start` or `end` alignment options. |
alignFlip | boolean | true | Flip alignment when colliding with boundary.
May only occur when `prioritizePosition` is true. |
avoidCollisions | boolean | true | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
collisionBoundary | Element | (Element | null)[] | null | [ ] | The element used as the collision boundary. By default this is the viewport, though you can provide additional
element(s) to be included in this check. |
collisionPadding | Padding | 0 | The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for
all sides), or a partial padding object, for example: { top: 20, left: 20 }. |
arrowPadding | number | 0 | The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it
from overflowing the corners. |
hideShiftedArrow | boolean | true | When `true`, hides the arrow when it cannot be centered
to the reference element. |
sticky | 'partial' | 'always' | 'partial' | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at
least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
hideWhenDetached | boolean | false | Whether to hide the content when the trigger becomes fully occluded. |
positionStrategy | 'fixed' | 'absolute' | 'fixed' | The type of CSS position property to use. |
updatePositionStrategy | 'always' | 'optimized' | 'optimized' | Strategy to update the position of the floating element on every animation frame. |
disableUpdateOnLayoutShift | boolean | false | Whether to disable the update position for the content when the layout shifted. |
prioritizePosition | boolean | false | Force content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElement | — | The custom element or virtual element that will be set as the reference to position the floating element. If
provided, it will replace the default anchor element. |
trapFocus | boolean | modal | Whether focus is trapped inside the popup while open (Tab cycles within the layer and body
scroll is locked for modal layers). |
disableHoverableContent | boolean | false | When `true`, hovering the popup closes instead of keeping it open (the grace area is disabled,
so leaving the trigger closes the layer immediately). |
onGracePointerExit | (() => void) | — | Callback invoked when the pointer finally leaves the grace area. Lets an upper layer (e.g.
Tooltip) run area-scoped close logic without registering its own second grace area. |
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
HoverCardRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | — | The open state of the hover card when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the hover card. |
openDelay | number | — | The duration from when the pointer enters the trigger until the hover card opens. |
closeDelay | number | — | The duration from when the pointer leaves the trigger or popup until the hover card closes. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | — |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the hover card changes. |
HoverCardTrigger
Notes
Architecture and benchmark differences
HoverCardCompact owns the trigger/positioner/popup/arrow composition while every primitive stays style-free and only the UI wrapper injects the hoverCardVariants classes. This mirrors radix-ui/shadcn-ui’s headless split, built on the shared Popper primitives. Ant Design, Element Plus, Mantine and Naive UI ship a single styled popover used for hover previews; VeanUI exposes a dedicated hover-card with configurable openDelay/closeDelay, per-slot *Props, an arrow toggle and a size scale the single-package libraries generally omit.
| Capability | VeanUI | shadcn/ui | Ant Design | Element Plus | Mantine | Naive UI |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| Open/close delay | ✅ | ✅ | — | — | ✅ | — |
| Popper placement (12) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Arrow | ✅ | ✅ | — | — | — | — |
| Sizes (6) | ✅ | — | — | — | — | — |
| Focus trigger | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
— = unsupported or a different interaction model (hover previews fold into the generic popover).
Cautions
- The card opens on hover and keyboard focus; tune
openDelay/closeDelayto reduce accidental triggers (defaults 700ms/300ms). - The popup is rendered through a
Portal(teleported todocument.body); position it inside a relatively-positioned ancestor only withportalProps: { disabled: true }. showArrowrenders an arrow;arrowPropscan override its position/classes.- The hover card is non-modal and dismisses on Escape or outside interaction; it does not trap focus.
Roadmap
N/A — hover-card is feature-complete for the current parity set.
FAQ
How do I adjust the open/close timing?
Set open-delay and close-delay (ms):
<SHoverCard :open-delay="200" :close-delay="100">
<template #trigger><SButton>Hover me</SButton></template>
<div>Preview content</div>
</SHoverCard>How do I position the card?
Use placement:
<SHoverCard placement="top">
<template #trigger><SButton>Hover me</SButton></template>
<div>Preview content</div>
</SHoverCard>How do I hide the arrow?
Set show-arrow={false}:
<SHoverCard :show-arrow="false">
<template #trigger><SButton>Hover me</SButton></template>
<div>Preview content</div>
</SHoverCard>How do I control the open state?
Bind open with v-model or use defaultOpen:
<SHoverCard v-model:open="open">
<template #trigger><SButton>Hover me</SButton></template>
<div>Preview content</div>
</SHoverCard>