Skip to content

Identity & micro

Identity components represent who or what in the UI: the person behind an account (Avatar), a grouped, recognizable block of content (Card), and the semantic typography of a keyboard shortcut (Kbd). They're small, high-frequency pieces — they show up in lists, headers, feeds — so their consistency defines the app's "face".

Reach for this page when you need to show an entity or group content, not when you need an action (actions) or pure layout (layout).

Avatar

When to use: visually represent a user/entity in lists, comments, headers — with a photo when available and colored initials as fallback.

A user's photo with an automatic fallback to colored initials when there is no src or the image fails to load. Initials are derived from name (not from alt).

<Avatar src={user.photo} name={user.name} alt={user.name} />;
<Avatar size="lg" status="online" name="Ann" />;
<Avatar name="John Smith" />; // fallback generates the initials "JS"
<Avatar name="John" status="busy" size="sm" />;
Prop Type Default
src string
alt string (image alt text)
name string (drives the fallback initials) ""
size "xs" \| "sm" \| "md" \| "lg" \| "xl" "md"
status "online" \| "offline" \| "busy"
onClick () => void

Initials come from name, not alt

The initials fallback is computed from name. If you pass only alt, the avatar shows ? when the image fails. For a multi-word name it uses the first letter of the first and last term ("John Smith""JS").

Always provide alt when there's a src

When src is set, alt is what screen readers announce. Describe the person (the name), not the media — avoid "photo of…".

Card

When to use: group related content into a block with visual elevation — a list item, a dashboard panel, a container for a table.

A container with header slots (title + actions) and a footer.

<Card title="Order #12345" actions={<Button variant="ghost">Edit</Button>}>
    Card content.
</Card>;

<Card elevation="raised" interactive onClick={() => navigate("/x")}>
    Clickable card with a hover effect.
</Card>;

<Card flush footer={<Pagination ... />}>
    <Table ... />
</Card>;
Prop Type Default
title ReactNode
actions ReactNode (right slot of the header)
footer ReactNode
elevation "flat" \| "default" \| "raised" \| "elevated" "default"
interactive boolean (cursor pointer + hover ring) false
flush boolean (zero internal padding — to host a Table) false

Use flush to host tables and lists

Cards have internal padding by default. When placing a Table or a list that already has its own margins, turn on flush so the content reaches the card edges without doubled padding.

interactive makes the whole card a button

With interactive, the card gets role="button", tabIndex={0}, and keyboard handling (Enter/Space). Avoid putting other clickable elements inside an interactive card — nested clicks compete for the same gesture and confuse keyboard navigation.

Kbd

When to use: display a key or combination (shortcuts, command-palette hints) with the look of a physical key.

A <kbd> styled for keyboard shortcuts.

<p>Press <Kbd>Ctrl</Kbd>+<Kbd>K</Kbd> to open the command palette.</p>
<Kbd size="lg"></Kbd>
Prop Type Default
size "sm" \| "md" \| "lg" "md"

One <Kbd> per key

For combinations, repeat the component instead of merging it all into plain text: <Kbd>Ctrl</Kbd>+<Kbd>K</Kbd>. Each <Kbd> renders a semantic <kbd> element that screen readers announce individually.

AvatarGroup

When to use it: show several people in a small space — meeting participants, task assignees, team members.

An overlapping row of avatars with a +N chip at the end.

<AvatarGroup
  label="Participants"
  max={3}
  items={[
    { name: "Ada Lovelace", src: ada },
    { name: "Grace Hopper" },
    { name: "Alan Turing" },
    { name: "Edsger Dijkstra" },
  ]}
  onOverflowClick={() => setDrawerOpen(true)}
/>
Prop Type Default What it does
items AvatarGroupItem[] People in the group ({ name, src? }).
max number 4 How many avatars before collapsing into +N.
size AvatarSize "md" Size applied to the avatars and the chip.
label string Accessible name of the group.
onOverflowClick () => void Makes the +N chip a focusable button (e.g. open "see all").

One group, one accessible name

The row is a single role="group" with one name, and each avatar exposes its person's name. Announcing seven unrelated images is noise; the +N chip carries the remaining count, so the total is never hidden from a screen reader.

The overlap is tunable

--tempest-avatar-overlap controls how much each avatar covers the previous one — the defaults are proportional to size.

Recap

Component Use for
Avatar Represent a user (photo or initials)
Card Group related content into an elevated block
Kbd Display keys / keyboard shortcuts

Key accessibility points:

  • Avatar.alt describes the user (the name), not the media; initials come from name.
  • Card with interactive applies role="button" + keyboard (Enter/Space) — don't nest other clickables.
  • Kbd: repeat one per key in combinations.

Related: actions (Button inside Card.actions) · data (Card flush hosting a Table) · layout (arranging cards in a grid/stack).