Headless / Overlay

HoverCard

HoverCard is Tooltip-shaped but built for interactive content. It opens on hover or focus, and a grace period lets the cursor travel from trigger to card without it vanishing.

Loading playground…
Updated 3 days agoEdit on GitHubWeb & native

What it is

A compound component for hover-summoned panels that hold real content — links, buttons, a small profile. It opens after openDelay on hover or focus and closes after closeDelay when the pointer leaves. The close delay is the hover bridge: it keeps the card open long enough for the user to move into it, and entering the card cancels the close.

The line against Tooltip is interactivity. A Tooltip holds a label and nothing focusable; a HoverCard holds content the user can act on.

Install

example.tsx
yarn add @usemotif/headless
example.tsx
import { HoverCard } from '@usemotif/headless';

Anatomy

example.tsx
<HoverCard.Root>
<HoverCard.Trigger>
  <a href="/u/jane">@jane</a>
</HoverCard.Trigger>
<HoverCard.Content>
  <Profile id="jane" />
</HoverCard.Content>
</HoverCard.Root>

API

HoverCard.Root

HoverCard.Root

stable
function HoverCard.Root(props: HoverCardRootProps): JSX.Element
openDelaynumber= 700

Milliseconds to wait before opening on hover. Longer than Tooltip — hover cards open less eagerly.

closeDelaynumber= 300

Milliseconds to wait before closing after the pointer leaves. This is the hover-bridge grace period.

placementPlacement= "bottom"

Where the card sits relative to the trigger.

childrenReactNode

The Trigger and Content parts.

HoverCard.Trigger

HoverCard.Trigger

stable
function HoverCard.Trigger(props: HoverCardTriggerProps): JSX.Element
childrenReactElementrequired

A single element. HoverCard clones it to attach the hover and focus handlers.

HoverCard.Content

HoverCard.Content

stable
function HoverCard.Content(props: HoverCardContentProps): JSX.Element | null
offsetnumber= 8

Pixel gap between the trigger and the card.

styleCSSProperties

Inline style for the card — positioning is applied on top.

childrenReactNode

The card content.

Accessibility

HoverCard opens on hover and on focus, so a keyboard user tabbing to the trigger gets the card too. Escape closes it. The content is supplementary by design — a preview, a shortcut to detail — so nothing essential should live only inside a HoverCard.

A hover or focus trigger cannot be reached by a touch user at all. Treat the card as an enhancement: the trigger itself (the @jane link, the thumbnail) must lead somewhere on a plain tap, with the card adding context for users who can hover.

Examples

A user-mention preview:

example.tsx
<HoverCard.Root openDelay={500}>
<HoverCard.Trigger>
  <Link href={`/u/${user.handle}`}>@{user.handle}</Link>
</HoverCard.Trigger>
<HoverCard.Content style={{ padding: 16, width: 280 }}>
  <HStack gap="$3">
    <Avatar name={user.name} src={user.avatar} />
    <VStack gap="$1">
      <Heading level={4}>{user.name}</Heading>
      <Text>{user.bio}</Text>
    </VStack>
  </HStack>
</HoverCard.Content>
</HoverCard.Root>