Cloud|

@k2b/ui

AvatarStack

Overlapping faces that open the full list of people.

AvatarStack@k2b/ui

Open the stack for all people; the viewer appears last.

TSX

Copy
<AvatarStack title="Here now" label="Here now: Jana, Paul, Mira and Tom" people={people} />

AvatarStack shows a compact group of people as one button that opens the full list. The application supplies identity, presence details, and selection behavior.

Use AvatarStack

Pass people, a list title, and an accessible label that describes who is present. The stack shows up to max faces (three by default), then “+n”. A person marked self stays out of the faces and appears last in the list, separated by a gap. The list count includes everyone, including self.

Use src for a picture, icon for a semantic fallback, and shape="square" for an agent or service account. Without a picture or icon, the avatar uses initials. badge follows the name; detail is one truncated line below it, optionally preceded by detailIcon. trailing holds short text such as “2 tabs”. hint adds one quiet line below the list.

A person with onSelect becomes a button. The list closes before the callback runs. Omit it for a person whose row has no destination.

Import

tsx
import { AvatarStack, type AvatarStackPerson } from "@k2b/ui";

Example

tsx
import { AvatarStack } from "@k2b/ui";

export const people = (
  <AvatarStack
    title="Here now"
    label="Here now: Jana Berger and Stock bot"
    hint="Select a person to jump to their cursor."
    people={[
      { id: "jana", name: "Jana Berger", detail: "Writing · Intake", detailIcon: "ti ti-pencil", onSelect: () => {} },
      { id: "bot", name: "Stock bot", shape: "square", icon: "ti ti-robot", badge: "Agent" },
      { id: "me", name: "You", self: true },
    ]}
  />
);

API reference

ts
type AvatarStackPerson = {
  id: string; name: string; src?: string | null; icon?: string; shape?: AvatarShape;
  badge?: JSX.Element; detail?: JSX.Element; detailIcon?: string; trailing?: JSX.Element;
  self?: boolean; onSelect?: () => void;
};

type AvatarStackProps = {
  people: readonly AvatarStackPerson[]; title: string; label: string; hint?: JSX.Element;
  max?: number; class?: string;
};

AvatarShape is "circle" | "square"; its default is "circle".

Accessibility

The trigger is one labelled button with expanded state and a relationship to its dialog. Decorative faces do not repeat their names to screen readers. The open list shows every person's name. Selectable rows are native buttons; other rows remain text. When a focused row stops being selectable or its person leaves, focus moves to the nearest selectable row, or to the list. Keyboard dismissal returns focus to the trigger. The open list follows its trigger when the window resizes or scrolls, so it stays inside the window.

Runtime

Faces and the list content render on the server. Opening the list and selecting a person require browser code. Below 48rem the list opens in a bottom sheet; otherwise it uses a popover beside the trigger.

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.