Avatar
An avatar shows an image, or the initials from name while it loads and
after it fails. The root is role="img" named by alt / name plus the
status, so everything drawn inside is decorative. sm / md / lg are the
control heights, so avatars line up with the controls beside them.
import Avatar from '@zuilib/components/avatar'
Image, initials, fallback
Without src (or after a load error) the initials show on the muted
surface; fallback overrides them; with nothing to show, a person icon.
Sizes and shapes
Status
Each status is a shape as well as a colour: online a solid disc, away a
crescent, busy a disc with a bar, offline a hollow ring. The status is
appended to the accessible name and shown as the dot's title.
Group
Avatar.Group overlaps its avatars, passes down size / shape, and
rings each one in the surface colour (--avatar-ring, falling back to
--background). max collapses the rest into a +N avatar named for
assistive technology and titled with the hidden names.
Props
AvatarProps extends span props (minus children).
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | Image URL. The fallback shows until it loads; on error it stays. A failed URL is not retried until src changes or the avatar remounts |
name | string | — | Source of the initials ("Ada Lovelace" → AL) and the default alt |
alt | string | name | Accessible name (role="img"). '' makes the avatar decorative, status included; with neither and no aria-label / aria-labelledby it is decorative too |
fallback | ReactNode | initials, else a person icon | Shown when there is no image; decorative |
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Inherits Avatar.Group |
shape | 'circle' | 'square' | 'circle' | Inherits Avatar.Group. A radius in className reaches the image too |
status | 'online' | 'offline' | 'busy' | 'away' | — | Presence dot on the bottom-end corner |
statusLabel | string | capitalised status | Appended to the accessible name and shown as the dot's title |
imgProps | Omit<ImgHTMLAttributes, 'src' | 'alt' | 'onLoad' | 'onError'> | — | Spread on the <img> (loading, srcSet, crossOrigin, …) |
className | string | — | Merged last, after the size and shape classes |
Avatar.Group
div props plus:
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Size of every avatar inside; an avatar's own size wins |
shape | 'circle' | 'square' | 'circle' | Shape of every avatar inside; an avatar's own shape wins |
max | number | — | How many to show; the rest collapse into a +N avatar |
overflowLabel | (hidden: number, names: string[]) => string | `${hidden} more: ${names}` | Accessible name of the +N avatar |
className | string | — | Merged last onto the root |
getInitials(name) is a named export.
Slots
| Slot | Element | Notes |
|---|---|---|
[data-slot="avatar"] | span | Root. Also data-size, data-shape, data-status, data-state="loading" | "loaded" | "error" | "fallback"; the overflow avatar adds data-overflow |
[data-slot="avatar-image"] | img | alt="", aria-hidden |
[data-slot="avatar-initials"] | span | Initials from name |
[data-slot="avatar-fallback"] | span | The fallback node |
[data-slot="avatar-icon"] | svg | Person icon when nothing else is available |
[data-slot="avatar-status"] | span | data-status; aria-hidden |
[data-slot="avatar-group"] | div | role="group", data-size, data-shape |
/* Consumer CSS: ring avatars in the card colour when they sit on a card */
[data-slot="card"] {
--avatar-ring: var(--card);
}
Tokens
| Token | Used for |
|---|---|
--control-height-sm/md/lg | Box per size; xs / xl step one --spacing notch outside |
--muted, --muted-foreground | Fallback surface and text |
--radius-md | The square shape |
--avatar-ring (falls back to --background) | Group ring and the status dot cut-out |
--success, --warning, --destructive, --destructive-foreground | Status dot colours |
Accessibility
- The root is
role="img"witharia-labelfromalt(orname) and the status; the<img>inside hasalt=""andaria-hidden. alt="", or no name at all, makes the avatararia-hidden;aria-labelledbykeeps it named.- Status is never colour-only: each state has its own shape, and the status text is part of the accessible name and the dot's
title. Avatar.Groupisrole="group"; the+Navatar is named byoverflowLabel(default"N more: names").- A cached image that finishes before hydration is read off the element, so the fallback does not flash over a loaded picture.