Skip to content
Nana UI

Layout & Containers

Hover Card

A rich preview that opens when the pointer rests on a trigger or the trigger takes focus. Unlike a tooltip it holds real content — a profile, a link preview — and stays open while the pointer crosses from the trigger onto the card.

View .md Download

A rich preview that opens when the pointer rests on a trigger or the trigger takes focus. Unlike a tooltip it holds real content — a profile, a link preview — and stays open while the pointer crosses from the trigger onto the card.

Rest the pointer on the name, or tab to it.

Rent for Unit 4B is overdue — Kamau Juma Kamau Juma Unit 4B · Tenant since March 2024
KES 42,000 overdue · last paid 5 Aug
Message Record payment
has not paid since August.
npx @nana-tec/ui add hover-card

Lands as src/components/ui/hover-card/hover-card.tsx, styled with Tailwind classes that resolve to the design tokens. Requires Tailwind v4.

import { HoverCard } from "@/components/ui/hover-card";
export function Example() {
return (
<HoverCard
placement="bottom"
content={
<div>
<strong>Ada Lovelace</strong>
<p>Wrote the first published algorithm.</p>
</div>
}
>
<a href="/people/ada">@ada</a>
</HoverCard>
);
}

It takes exactly one child element and clones it to attach the hover, focus and Escape handling plus aria-describedby — so the trigger stays your element, not a wrapper.

Classes you pass are merged with tailwind-merge, so an override wins rather than fighting. In React, classNames targets the root and card parts individually. See Using Tailwind.

SlotHoldsSet in
mediaAn avatar or a photograph, cropped to a 44px circle — set your own border-radius for a squareBeside the title
titleThe headingSemibold, at the heading-4 size
subtitleA line under the headingLabel size, medium weight, muted
contentThe body — a sentence, a status, a list of factsBody size and weight
actionsButtons, on a quiet footerRight-aligned; actions-align="start" moves them left

Every part is optional and a part left empty takes no space, so a card can be a single sentence or a full profile. Nothing on it is set lighter than the system’s medium body weight.

Use size="sm" buttons, with one primary at most and the rest tertiary; the solid secondary amber is louder than a card this size wants. Keep the buttons to what the reader would do next from this preview — anything they need in order to act must also be reachable without hovering.

The card opens after openDelay (700ms by default) and closes after closeDelay (300ms by default) — open-delay and close-delay on the Web Component. The close delay is the grace period that lets the pointer leave the trigger and reach the card without it disappearing; once the pointer is on the card, the card stays open until the pointer leaves both.

The longer open delay is deliberate: a hover card carries more than a tooltip, so it should not appear for a pointer that is only passing over.

The React and Svelte props, read from the components’ own types:

Trigger

PropType
onMouseEnter(event: MouseEvent<HTMLElement>) => void
onMouseLeave(event: MouseEvent<HTMLElement>) => void
onFocus(event: FocusEvent<HTMLElement>) => void
onBlur(event: FocusEvent<HTMLElement>) => void

HoverCard

PropTypeDefault
contentrequiredReactNode
placementHoverCardPlacement'bottom'
disabledbooleanfalse
openDelaynumber
closeDelaynumber
childrenrequiredReactElement<Record<string, unknown>>

Also accepts Styled<HoverCardPart>.

The attribute names, events and CSS hooks below are the Web Components reference.

Properties

PropertyAttributeTypeDefaultDescription
actionsAlignactions-align'start' | 'end''end'Which end of the footer the buttons sit at
closeDelayclose-delay—DEFAULT_CLOSE_DELAYHow long the card waits for the pointer to come back before closing, in ms
disableddisabledbooleanfalseNever open
openopenbooleanfalseWhether the card is showing
openDelayopen-delay—DEFAULT_OPEN_DELAYHow long the pointer must rest before the card opens, in ms
placementplacementHoverCardPlacement'bottom'Which side of the trigger the card opens on; it flips when there is no room

Events

EventDescription
nana-showThe card opened
nana-hideThe card closed

Slots

SlotDescription
(default)The trigger
mediaAn avatar or photograph beside the title
titleThe card's heading
subtitleA line under the heading
contentThe body
actionsButtons along the foot of the card

CSS parts

PartDescription
::part(base)The wrapper around the trigger
::part(card)The floating card
::part(header)The media, title and subtitle
::part(media)The media area
::part(title)The heading
::part(subtitle)The line under the heading
::part(body)The body
::part(footer)The actions row

CSS custom properties

PropertyDescription
--nana-hover-card-widthWidest the card grows
--nana-hover-card-paddingSpace inside each part of the card
ActionDoes
Focus the triggerShows after openDelay
Tab into the cardKeeps it open (Web Components)
BlurHides after closeDelay
EscapeHides at once, however the card was opened

The card is linked to the trigger through aria-describedby while it is open — the title, subtitle and body on the Web Component — so a screen reader announces it as a description of the trigger. Buttons on the card are reached by moving focus on from the trigger; the card stays open while focus is inside it. Keep anything essential reachable without the card: it is a preview, and touch users never hover. The Web Component ignores touch outright, so a tap goes straight to whatever the trigger does.

Explore Hover Card interactively Every state, prop, and edge case — with live controls — in Storybook.
View in Storybook