Avatar
Image, initials, or glyph standing for a person or entity, where the accessible name identifies who is represented and stays the same whichever branch of the fallback chain renders.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when an image, initials, or a glyph identifies a specific person (e.g., a comment author, an account holder, a viewer in a list) or a non-person entity (e.g., a channel, a team, or a brand account).
Try a different component when
- Do not use when activating the avatar performs an action or navigates elsewhere (use
button.basicorlink.basic). - Do not use when a status, presence, or count indicator sits on or beside the avatar (use
badge.basic). - Do not use when avatars are stacked into a group with an overflow affordance such as "+3" (use
avatar.group). - Do not use when the image is content the user is meant to look at rather than an identifier for someone, such as a poster, a thumbnail, or artwork (use
collection-row.basicwhen it sits in a browse row).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- When a visible name for the same person sits beside the avatar, the avatar is decorative:
alt=""on an<img>, oraria-hidden="true"on any other rendering. - When the avatar is the only identification of the person or entity, it carries an accessible name, and that name is the person's or entity's name.
- A photograph, an initials fallback, and a generic glyph for the same person carry the same name.
- An
<img>avatar is named withalt. - A rendering that is not an
<img>usesrole="img"named witharia-labeloraria-labelledby.- Descendants of
role="img"are presentational. Initials or an inline<svg>inside it are not exposed, so the name comes from the attribute.
- Descendants of
Donts
Avoid these accessibility and UX barriers.
- Do not make the avatar focusable or place it in the page tab sequence.
- Do not use a file name, "avatar", "image", "photo", or "profile picture" as the accessible name.
- Do not place text inside
role="img"and rely on it being announced. Browsers applypresentationto every descendant of that role. - Do not pair
alt=""with anaria-label. Supplying a name by any mechanism restores the implicit image role, and the avatar stops being decorative. - Do not omit
alton an<img>avatar. An absentaltleaves the image unnamed rather than decorative. - Do not name only the branch that usually renders, leaving the fallback anonymous.
Customizable
Alternatives and options that give the AI agent some room to move.
- Which element renders the artwork. An
<img>, an inline<svg>, a<span>of initials, and an element carrying a CSSbackground-imageare all acceptable. An<img>is named withalt; every other rendering carriesrole="img"and anaria-label. - Where the name lives on a rendering that is not an
<img>.role="img"witharia-labelis preferred. When the artwork must stay out of the accessibility tree entirely,aria-hidden="true"on the artwork with the name supplied as visually hidden text (seeglobal.sr-only) is an acceptable substitute. - The fallback chain. A photograph, a monogram, a generic glyph, or a color derived from the identifier, in any order, as long as the name is the same at every branch.
- Which form of the name is used, whether a display name, a full name, or a username, as long as it matches the form used for the same person elsewhere in the view.
- Shape, size, border treatment, and how the image is cropped.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
"use client";
// Visually-hidden styles matching the global sr-only utility (global.sr-only).
const srOnly = {
clip: "rect(1px, 1px, 1px, 1px)",
height: "1px",
overflow: "hidden",
position: "absolute",
whiteSpace: "nowrap",
width: "1px",
};
export function Avatar({ name, src, decorative = false }) {
const [failed, setFailed] = useState(false);
if (src && !failed) {
return <img src={src} alt={decorative ? "" : name} onError={() => setFailed(true)} />;
}
if (decorative) {
return <span aria-hidden="true">{monogram(name)}</span>;
}
// role="img" makes descendants presentational: the initials are never
// announced, so aria-label is the only thing naming this.
return (
<span role="img" aria-label={name}>
{monogram(name)}
</span>
);
}
function monogram(name) {
return name
.split(" ")
.map((part) => part[0])
.join("");
}
function AvatarExamples() {
return (
<>
{/* Visible name present, so the avatar is decorative. */}
<p>
<Avatar name="Jane Okonkwo" src="/avatars/jane.jpg" decorative />
<span>Jane Okonkwo</span> commented on your clip.
</p>
{/* No visible names, so each avatar is named. Marcus has no photo and
reports the same name from the initials branch. */}
<ul>
<li>
<Avatar name="Jane Okonkwo" src="/avatars/jane.jpg" />
</li>
<li>
<Avatar name="Marcus Bell" />
</li>
<li>
<Avatar name="Priya Raman" src="/avatars/priya.jpg" />
</li>
</ul>
{/* Alternative to Avatar's role="img" branch, not an addition to it, for
artwork that cannot carry a role such as a CSS background. The name
must sit outside the aria-hidden subtree or it is never announced. */}
<span>
<span aria-hidden="true" style={{ background: "url(/avatars/jane.jpg)" }} />
<span style={srOnly}>Jane Okonkwo</span>
</span>
</>
);
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Structure
- Tab never lands on the avatar.
- A decorative avatar exposes no role and no accessible name.
Accessible naming
- A meaningful avatar's accessible name is the person's or entity's name.
- The name contains no file name and none of the words "avatar", "image", "photo", or "profile picture".
- Blocking the image so the fallback renders produces the same name the photograph produced.
Screen Reader
- An avatar beside a visible name announces the person once, not twice.
- Initials inside a
role="img"avatar are not announced as separate text.
Visual
- A glyph or monogram carrying meaning is distinguishable from its background, per
global.non-text-contrast.