Avatar
betav0.1.0A person, or an entity identified by its name: a round disc holding two initials or a photo, optionally followed by the person's name and a one-line description. AvatarGroup, exported alongside, stacks several people as overlapping discs followed by a `+N` counter. Use Avatar for a person or an entity identified by its name — never for an icon, an event type, a status, a count or a product logo (use Thumbnail, Icon, Badge or Chip). Pass `name`, never pre-computed initials; omit `name` when the person is unknown and Avatar draws the anonymous state. When the host already renders the name next to the avatar (a CardHeader title, a table cell), pass `aria-hidden` so the name is announced once; when nothing else renders it, use `showName`. Size by context: lg in a Card header or a profile header, md in lists, rows and presence groups, sm in dense tables and inline mentions, xs only beside caption-sized text. Use `withTooltip` only when the name is visible nowhere else, and never wrap Avatar in a host Tooltip. Several people means AvatarGroup, named by an `aria-label` saying who they are or by `aria-labelledby` (one is required). Never override the fill, the size, the group ring or the 220px name cap with className.
Variants
Examples
<Avatar name="Ada Lovelace" /><Avatar name="Ada Lovelace" src={user.avatarUrl} size="lg" /><Avatar name="Ada Lovelace" size="md" showName description="Manager of PM - OpenAEV" />// Unknown person: omit name.
<Avatar showName />// The name is visible nowhere else on screen.
<Avatar name={author} size="sm" withTooltip />// Card header: the title already shows the name, so the avatar is hidden from assistive technology.
<CardHeader icon={<Avatar name={author} size="lg" aria-hidden />}>
<CardTitle>{author}</CardTitle>
</CardHeader><AvatarGroup
aria-label="Users editing this entity"
items={users.map((user) => ({ id: user.id, name: user.name, src: user.avatarUrl }))}
/>// Migration from MUI: <Avatar sx={{ bgcolor: stringToColour(name) }}>{name.charAt(0)}</Avatar>
<Avatar name={name} />Props
| Prop | Type |
|---|---|
name | string — the person's name. Source of the initials (two letters: first letters of the first two words, or the first two letters of a single word) and of the accessible name. Omit it when unknown. |
src | string — photo URL. The initials show while it loads and stay if it fails. |
size | "xs" | "sm" | "md" | "lg" — 16 / 24 / 32 / 44 px. Default "md". |
showName | boolean — render the name beside the disc. Default false. The disc is then decorative. |
description | ReactNode — one line under the name (lg, md) or after it (sm, xs). Ignored without showName. |
anonymousLabel | string — label of a person with no name. Default "Unknown". Products localise it. |
withTooltip | boolean — show the name in a tooltip and make the disc focusable. Default false. Ignored with showName. |
AvatarGroup.items | { id: string; name?: string; src?: string }[] — people to show, in order. The first is drawn on top. `id` is the person's stable identity (a user id), never the array index: it keeps focus and an open tooltip on the same person when the group changes. |
AvatarGroup.max | number — avatars shown before `+N`. Default 4. |
AvatarGroup.overflowLabel | (hiddenNames: string[]) => string — tooltip text and accessible name of `+N`. Default "3 others: A, B & C". Products localise it. |
AvatarGroup.anonymousLabel | string — label of an item with no name. Default "Unknown". |
AvatarGroup.aria-label | string — required unless aria-labelledby is set: say who the people are, e.g. "Users editing this entity". |
AvatarGroup.aria-labelledby | string — required unless aria-label is set: id of the visible text that says who the people are. |
Accessibility
Semantics: a disc with no visible name is role="img" named by the person (or anonymousLabel); its photo is alt="" so the name is announced once and does not change while the photo loads or fails. With showName, or when the host passes aria-hidden, the disc carries no role and no label. Keyboard: a standalone avatar is not focusable unless withTooltip; in AvatarGroup every visible disc and `+N` is a Tab stop (Avatar.rfc.md §9 Q10) and opens its tooltip on focus, Escape closes it (Radix Tooltip, WCAG 1.4.13). The focused disc is lifted above its neighbours so the ring is never covered. While a tooltip is open Radix adds aria-describedby, so the name is heard a second time as a description — accepted, the tooltip repeats the name by design. Focus ring: the standard ring-focus / ring-offset-focus pattern, no deviation. The group ring (border-bg-elevation-default) is decorative separation, not a WCAG 1.4.11 boundary: each disc is identified by its initials or photo. Colour carries no meaning (one colour for everyone). Measured by pnpm check:contrast (2026-10-13): initials dark #18191b on #42caff = 9.32:1 ✅, light #f2f2f3 on #0015a8 = 11.22:1 ✅. Name (text-default-primary) dark 17.38 / 15.98 / 14.28 / 10.25:1 on layers 0–3, light 15.72 / 17.59 / 16.02 / 13.96:1 ✅. Description and `+N` (text-default-secondary) dark 8.99 / 8.27 / 7.38 / 5.30:1 on layers 0–3, light 7.89 / 8.83 / 8.04 / 7.00:1 ✅. Focus ring (brand-primary against ring-offset-focus, which follows the layer) dark 10.30 / 9.47 / 8.46 / 6.07:1 on layers 0–3, light 11.22 / 12.55 / 11.43 / 9.96:1 ✅.