KQ-1277 · TEAM/FAN

Team Fan

A team card whose overlapping faces fan into a readable row on hover or keyboard focus: each face springs to its slot on springs.snap a cascade beat after the last, its first name rolls up beneath it and its presence dot pops in on flick, the "+12" bubble flips about its axis to the full count and an invite button springs out to the end; leaving gathers them back in reverse on glide. Pressing a face lifts it on snap while the rest recede on glide, and opens a details disclosure whose measured height glides open with role, presence, local time and Message. The row is a toolbar with a roving tabindex: arrows move, Home and End jump, Enter focuses a person and moves focus into the details, Escape hands it back.

KQ-1277 · TEAM/FAN

Platform crew

Basinworks · Design systems

5 online

18 members, 5 online

18 members · 5 online · hover or tab in

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/team-fan

Props

members
TeamMember[]
default defaultTeam
Everyone on the team (id, name, role, presence, utcOffset, image); the first max are the faces in the stack.
total
number
default members.length
The team's size when members is only a page of it; the count bubble reads it.
max
number
default 6
Faces in the stack before the count bubble; four under 26rem of container width.
title
string
default "Platform crew"
The card's heading, and the toolbar's name.
subtitle
string
default "Basinworks · Design systems"
The line under the heading.
headingLevel
2 | 3 | 4
default 3
The heading's level.
value / defaultValue
string | null
default null
The focused member's id, controlled or initial; null when nobody is focused.
onValueChange
(id: string | null) => void
default —
Fires from the press that focused a face or let it go.
onInvite
() => void
default —
The invite button at the end of the fan.
onShowAll
() => void
default —
The count bubble: show everyone.
onMessage
(id: string) => void
default —
Message, in a focused person's details.
inviteLabel
string
default "Invite"
The invite button's label, under it in the fan.
countLabel
string
default "members"
The word under the flipped count.
spread
number
default 0.5
How much air the fan puts between faces, 0 to 1; always clamped to the card's width.
cascade
number
default 40
The beat between one face landing and the next, in ms.
focus
number
default 0.6
How far the rest of the team recedes behind a focused face, 0 to 1: opacity, scale and colour.
size
"sm" | "md" | "lg"
default "md"
Faces 28, 36 or 44 px across.
now
Date | number
default 2 Oct 2026, 13:02 UTC
The clock each person's local time is read from.
accent
string
default "var(--accent-bright)"
The focused face's ring and the invite tint; any CSS colour.
sound
boolean
default false
A swish as a key or a tap opens the fan (hover opens it silently), a pop as a face is focused.
disabled
boolean
default false
Keeps the stack closed and its buttons inert.
className
string
default —
Classes for the article.