Hover Card
Primitive

Hover Card

Headless hover-and-focus preview primitives with delayed disclosure and floating positioning.

Live behavior

Hover Card in motion

Interactive
Atom behavior · App-owned appearance

Hover or focus the identity to reveal supporting information.

waiting for input

When to Use

Use HoverCard for supplemental, nonessential preview content such as a short profile summary. Use Tooltip for a brief label, Popover for content with buttons or other interaction, and Dialog for a task that must hold focus. HoverCard closes when focus leaves and does not create a dialog or popover accessibility relationship, so never put required actions inside it.

Features

The behavior Atom owns before your product adds appearance.

  • Supports controlled and uncontrolled open state.
  • Opens from mouse-capable pointer hover and focus-visible Trigger interaction; touch input and its browser-generated compatibility events do not open the preview or replace the Trigger's native action.
  • Does not install hover disclosure on touch-only devices and updates that capability when the browser's available input devices change.
  • Uses configurable open and close delays and a safe pointer corridor that keeps Content open while the pointer crosses the gap from Trigger to Content.
  • Positions centered Content by shifting within the viewport before considering placement fallbacks. Edge-aligned Content resolves alternate alignments before shifting, and both modes retain opposite- and perpendicular-side fallbacks; Arrow coordinates follow the resolved result.
  • Supports inline or custom-container portals.
  • Participates in topmost-layer Escape dismissal.

Import

tsx
import { HoverCard } from "@flowstack-ui/atom";

Anatomy

tsx
<HoverCard.Root>
  <HoverCard.Trigger />
  <HoverCard.Portal>
    <HoverCard.Content>
      <HoverCard.Arrow />
    </HoverCard.Content>
  </HoverCard.Portal>
</HoverCard.Root>

getHoverCardArrowGeometry()

API Reference

Root

Owns delayed open state and renders no wrapper.

PropTypeDefault
openboolean-
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void-
openDelaynumber700
closeDelaynumber300
disabledbooleanfalse

Trigger

Provides the positioning reference and mouse-capable hover/focus events. It renders a focusable span by default. With asChild, the child keeps its own tab order and native touch behavior.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"hover-card-trigger"
[data-state]"open" | "closed"

Trigger emits no aria-expanded, aria-controls, or dialog/popover role. Disabled Root prevents opening and removes the default Trigger from the Tab order, but Trigger emits no disabled ARIA or data attribute.

Portal

Moves Content to document.body by default without a wrapper.

PropTypeDefault
containerElement | DocumentFragment | nulldocument.body
disabledbooleanfalse

Content

Renders a positioned generic div. It has no owned landmark or popup role; ariaLabel is passed as aria-label but does not create a Trigger relationship. Content exposes measured --atom-floating-available-width and --atom-floating-available-height properties. Portalled Content preserves an explicit dir; otherwise it resolves direction from Trigger and then Direction.Provider.

PropTypeDefault
side"top" | "right" | "bottom" | "left""bottom"
align"start" | "center" | "end""center"
sideOffsetnumber8
ariaLabelstring-
ARIA attributeValues
aria-labelValue from ariaLabel
Data attributeValues
[data-slot]"hover-card-content"
[data-state]"open" | "closed"
[data-side]Resolved side after collision handling
[data-positioned]Present after positioning

Arrow

Renders a decorative SVG aligned to Content's resolved side. Custom children replace its default polygon.

PropTypeDefault
widthnumber10
heightnumber5
childrenReactNodeDefault polygon
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-hidden"true"
Data attributeValues
[data-slot]"hover-card-arrow"
[data-side]Resolved Content side

getHoverCardArrowGeometry

Returns SVG dimensions, outward size, and polygon points for a side, width, and height. This is useful when building a compatible custom Arrow.

The entry point also exports useHoverCardContext and useHoverCardContentContext for advanced custom parts. Both throw outside their required Root or Content provider.

Examples

Profile Preview

tsx
import { HoverCard } from "@flowstack-ui/atom";

export function ProfilePreview() {
  return (
    <HoverCard.Root>
      <HoverCard.Trigger asChild>
        <a href="/people/ada">Ada Lovelace</a>
      </HoverCard.Trigger>
      <HoverCard.Portal>
        <HoverCard.Content>
          <HoverCard.Arrow />
          <h2>Ada Lovelace</h2>
          <p>Mathematician and early computing author.</p>
        </HoverCard.Content>
      </HoverCard.Portal>
    </HoverCard.Root>
  );
}

Accessibility

HoverCard has no standalone WAI-ARIA popup pattern. Trigger remains the semantic element supplied by the consumer, Content is supplemental document content, and Arrow is hidden from assistive technology. The preview must not be the only place important information or actions are available. Touch does not open HoverCard, so a Trigger link keeps its normal tap and long-press behavior.

KeyDescription
Tab / Shift+TabFocus-visible entry can open Trigger; leaving closes after closeDelay.
EscapeCloses the topmost open HoverCard immediately.

Changelog

Unreleased

0.24.0

  • Added public Agent Knowledge for component selection, required composition, recurring mistakes, and validation.
  • Preserved centered Content alignment near viewport edges by shifting it into view before considering alternate placements.

0.6.9

  • Preserved resolved Trigger or provider direction on portalled Content while retaining explicit Content dir precedence.

0.6.8

  • Exposed measured available dimensions through headless floating properties for constrained styled surfaces.

0.6.6

  • Disabled hover interaction when the browser reports no hover-capable input and captured touch pointer entry before native compatibility mouse events on hybrid devices.

0.6.5

  • Added a short post-touch compatibility-event guard so intermittent Safari mouse and focus synthesis cannot open HoverCard after touch activation.

0.6.4

  • Limited hover disclosure to mouse-capable pointers so touch keeps the Trigger's native tap and long-press behavior.
  • Added a safe geometric corridor from Trigger to Content and prevented exit-retained Content from reopening itself during its closing motion.

0.6.3

  • Prioritized every usable alignment on the requested side, followed by the opposite side, before allowing perpendicular-axis collision fallbacks.

0.6.2

  • Added perpendicular-side collision fallbacks after the preferred and opposite sides, preventing wide previews from remaining cropped on a constrained axis.

0.3.1

  • Fixed exit-presence cleanup so closed HoverCard Content unmounts after its CSS motion window even when no end event is emitted.

0.2.0

  • Fixed HoverCard render/default trigger positioning by updating Floating UI after the trigger ref commits.
  • Made default and render HoverCard triggers keyboard focusable while keeping opening scoped to hover and focus-visible interactions.
  • Added shared dismissable layer Escape handling so HoverCard participates in topmost-layer dismissal with other overlays.

0.1.0

  • Initial Atom release.