Avatar Stack
Show a group of people as overlapping avatars, with presence and an expandable list.
import { AvatarStack, AvatarStackUser } from '@youj/react'
Installation & usage
npm i @youj/reactFor 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 an avatar stack to show who is in a document, a project or a call, when the faces matter more than the details. It shows the first max people and puts everyone else behind a "+N" button that opens the full list with each person's role and status. For a single person, use an Avatar. For a list people manage (roles, removal), use a table or a list, not a stack.
import { AvatarStack, type AvatarStackUser } from '@youj/react'
const people: AvatarStackUser[] = [
{ id: 'mira', name: 'Mira Chen', src: '/avatars/mira.jpg', presence: 'online', description: 'Design lead' },
{ id: 'jonas', name: 'Jonas Weber', presence: 'away' },
// …
]
export function Collaborators() {
return <AvatarStack users={people} label="Collaborators" />
}Each person needs an id and a name. Without a src, the avatar shows their initials. presence adds a status dot (online, away, busy or offline), and description is the second line in the full list. label names the group ("Collaborators: Mira Chen, Jonas Weber and 5 others") and heads the list.
On a card, set the rings that cut the overlaps out to the card's color, as the demo does: *:data-[slot=avatar]:ring-surface.
Description
A roadmap card whose collaborators sit in a stack, with a button that cycles one person's status.
The stack at sm, default and lg with different limits, and one that fits without a +N.
size follows the Avatar sizes, and max sets how many faces show before "+N". When only one person would be hidden, the stack shows them instead: a "+1" would take the same space as their face.
Code
avatar-stack-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsusers, max, size… (4)
Props
| Prop | Type | |
|---|---|---|
usersRequired | readonly AvatarStackUser[] | – |
max | numberAvatars shown before "+N". default 4 | 4 |
size | 'sm' | 'default' | 'lg'default 'default' | 'default' |
label | stringWhat the people are, for the group's name and the list heading. default "People" | "People" |
Other props pass through: extends ComponentProps<'div'>. | ||
MotionFan out, lift, presence dot… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
Fan outhovering the stack spreads each avatar 3px per position towards the inline end (translate) | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Liftthe avatar under the pointer rises 2px above its neighbors | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Presence dotpops in from scale 0 when a status changes | duration.baseease.enter220ms · cubic-bezier(0.16, 1, 0.3, 1) | 220mscubic-bezier(0.16, 1, 0.3, 1) | |
Name tooltipdrifts off the avatar (yj-tooltip), after a 200ms hover delay | no token | ||
Full listscales from "+N" as it fades in (yj-pop) | no token |
Reduced motion
No fan and no lift; status dots appear without the pop.
Only transforms move, so the stack never reflows the line it sits in.
Reduced motion
The stack doesn't fan out on hover, the hovered avatar doesn't lift and status dots appear in place. Tooltips and the list still open, with the overlay presets' reduced behavior.
AccessibilityKeyboard, announcements
Accessibility
- The stack is a
groupnamed with everyone in it: the first two names and a count of the rest. - Each avatar shows the person's name and status in a tooltip on hover. Avatars aren't in the tab order, so keyboard and screen reader users get the names from the group's label and the "+N" list.
- "+N" is a button named "Show all 7 collaborators". It opens the list in a Popover, where each status is written out, not only shown as a color.
- Presence dots are decorative; the status text is in the tooltip and the list.