Person Hover Card
Overlapping 24px avatars with a compact hover card per person (a 32px avatar, the name and a muted email) and a "+N" count; the stack lists everyone.
- Status
- Since
0.23.19- Accessibility pattern
- named buttons, hover card on hover and focus, tap on touch
Last updated
Install
Add Person Hover Card from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/person-hover-cardThe same command installs the registry items it composes: @vegastack/avatar, @vegastack/button, @vegastack/hover-card, @vegastack/popover, @vegastack/searchable-select.
Usage
import { AvatarStack } from "@/components/ui/person-hover-card";
<AvatarStack people={participants} label="Participants" />;AvatarStack stacks up to max (5) 24px avatars — overlapping, each ringed in the background
colour — with the rest counted in a "+N". Hovering an avatar previews that person: a compact 240px
card with a 32px avatar and, beside it, the name over a smaller muted email (always shown when
known). The stack is one button: click, tap or Enter lists everyone in the same format.
Someone without an account (a free-text participant such as "Northwind FM leads") has no email:
their avatar shows initials and the card just the name.
Anatomy
Examples
One person
PersonHoverCard wraps any trigger — usually a PersonAvatar — in a round ghost button named by
the person.
The card
PersonCard is the card's body (layout="card") and a compact list row (layout="row").
Inactive member
badge on the person puts a status right after the name — a string such as "Inactive" is a small
muted outline badge. It shows in PersonCard, the hover card and the AvatarStack list. For a
table's person cell, compose PersonAvatar with the name and PersonBadge.
API Reference
AvatarStack
| Prop | Type | Default | Description |
|---|---|---|---|
people* | readonly Person[] | — | The people, in order. |
className | string | — | Classes for the group. |
label | string | "People" | The group's accessible name. |
max | number | 5 | How many avatars show before the rest go behind "+N". |
Data attributes and CSS variables on AvatarStack
| Attribute | Values |
|---|---|
data-slot | "avatar-stack" |
PersonHoverCard
| Prop | Type | Default | Description |
|---|---|---|---|
children* | React.ReactNode | — | The trigger — usually a PersonAvatar. It is wrapped in a round ghost button named by the person. |
person* | Person | — | The person. |
className | string | — | Classes for the trigger button. |
PersonCard
| Prop | Type | Default | Description |
|---|---|---|---|
person* | Person | — | The person. |
layout | "card" | "row" | "card" | card — the hover card's body: a 32px avatar with the name and email stacked beside it.
row — one line of a list: a smaller avatar beside them. |
Data attributes and CSS variables on PersonCard
| Attribute | Values |
|---|---|
data-layout | mirrors a prop or state value |
data-slot | "person-card" |
PersonAvatar
| Prop | Type | Default | Description |
|---|---|---|---|
person* | Person | — | The person. |
className | string | — | Classes for the avatar. |
size | "default" | "lg" | "sm" | "sm" | Avatar size. |
Accessibility
- The stack is one button named
"{label}: {names}"; it opens the full list, so keyboard and touch users reach every person without the hover previews. PersonHoverCardon its own is a round button named by the person; its card opens on hover and on keyboard focus, a tap toggles it, and Esc closes it.
| Key | Action |
|---|---|
| Tab | Move to the stack |
| Enter / Space | List everyone (or open a person's card) |
| Esc | Close the list or the card |
| Contract | States tested |
|---|---|
| Behaviour | stack, overflow, hover-card |
| Accessibility | labeled, browser-accessibility-test |
| Visual | default, open |