Skip to content

Hover Card

Preview a link’s destination on hover or keyboard focus.

import { HoverCard, HoverCardContent, HoverCardTrigger } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a hover card to let people peek at a link's destination without leaving the page: mentions, references, commit hashes. It's a visual shortcut for sighted pointer, touch and keyboard users, and the link stays the real interface, so everything in the card must also be at its destination. Keep controls out of the card. For content people open on purpose, including on touch, use a Popover. For a one-line label, use a Tooltip.

import { HoverCard, HoverCardContent, HoverCardTrigger } from '@youj/react'

export function Mention() {
  return (
    <HoverCard>
      <HoverCardTrigger href="/people/priya">@priya</HoverCardTrigger>
      <HoverCardContent>
        <PersonSummary handle="priya" />
      </HoverCardContent>
    </HoverCard>
  )
}

HoverCardTrigger renders an <a>, so it stays a plain link: it navigates on click and reads as a link to assistive technology. Style it like the links around it.

Reference

PropsHoverCardTrigger passes through to Base UI's preview card trigger

Props

HoverCardContent

HoverCardContent adds no props of its own. They pass through to PreviewCardPrimitive.Popup.Props, Pick<PreviewCardPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'collisionPadding'>.

HoverCardTrigger passes through to Base UI's preview card trigger. Here it defaults delay to 500 and closeDelay to 250, and takes href and the other link attributes. HoverCard takes the root props (open, defaultOpen, onOpenChange).

TouchTouch has no hover, so on a touch device a tap on the link opens its…

Touch

Touch has no hover, so on a touch device a tap on the link opens its card instead of following the link. A second tap on the link follows it and closes the card, and a tap anywhere else closes the card without leaving the page. Base UI's preview card doesn't respond to touch on its own; HoverCardTrigger adds this. On a phone the card is still a preview: a screen reader user never sees it, so keep everything in it at the destination too.

MotionOpen, close, keyboard focus, Escape… · reduced motion

Motion

Motion tokens
MomentTokens
Openscale .96 → 1 and fade, out of the link (yj-pop)
duration.fastease.enter

140ms · cubic-bezier(0.16, 1, 0.3, 1)

140mscubic-bezier(0.16, 1, 0.3, 1)
Close
duration.instantease.exit

80ms · cubic-bezier(0.4, 0, 1, 1)

80mscubic-bezier(0.4, 0, 1, 1)
Keyboard focus, EscapeBase UI sets data-instant
no token

0ms

0ms
Open and close delayshover intent, not motion
no token

500ms, 250ms

500ms, 250ms

Reduced motion

The card appears and disappears at once. The delays stay the same.

Reduced motion

The card appears in place when the delay ends and closes at once. The delays are kept, because they separate a glance from a pass-by rather than animate anything.

AccessibilityKeyboard, focus, announcements

Accessibility

  • The trigger is an ordinary link, and it's the only accessible interface. The card isn't announced, and its content isn't in the tab order.
  • Keyboard focus on the link opens the card, so keyboard users get the same preview. Escape closes it and leaves focus on the link.
  • On touch, the first tap on the link shows the card and the second follows the link. The card's content isn't navigable by touch, keyboard or screen reader, which is why it must repeat what the destination says.
KeyAction
TabFocusing the link opens its card.
EnterFollows the link.
EscapeCloses the card and keeps focus on the link.