Avatar — accessibility
Keyboard
- Not focusable and not interactive. When it sits in a chip or a row, that host is the control.
- One exception, and it is the host again: on the person card the mark is wrapped in
button.tbar-mark.round, which opens the picture dialog. The button carries the name and the focus ring; the mark inside it stays inert.
ARIA
- Without a picture the mark is
aria-hidden="true": the initials are a compressed rendering of a name that is already in the DOM next to it, and reading "JN" before "Josef Novák" is noise. - With a picture it is not hidden, and the
<img>carriesalt=""— a photograph of a person whose name is already written beside the mark adds nothing a screen reader can use, so it is decorative for the same reason. titleis set to the full name, which surfaces on hover for a sighted mouse user; it is not a substitute for the name being present as text.- Consequence: an avatar must be accompanied by the name in text. On its own it is invisible to a screen reader, by design.
Focus
- Nothing focusable, so no focus ring. The chip or row around it keeps the foundation ring from
ds.css. - Where the mark is the upload trigger, the ring belongs to the button and follows its shape: round for a person's photo,
--radius-mdfor a company logo.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Ground is
--surface-75in both themes; the ring and initials arehsl(var(--av-hue) …), so the exact ratio varies with the hash. The palette is tuned to stay legible, but a specific hue is not guaranteed to clear AA for 11px text. - Because the mark is
aria-hiddenand always accompanied by the name, the initials are not load-bearing text — the name beside it is. - In the picture case the ring is
--border, a hairline that separates the picture from the page. It is decoration, not information: it is never the only thing telling a person or a company apart. - A photograph carries no contrast guarantee at all — whatever was uploaded is what shows. Nothing is written over it, so nothing can become illegible.
Known limits
- Hue comes from a hash, so two different people can land on the same colour; the colour is a memory aid, never an identifier.
- The colour is not announced and cannot be perceived by a user who cannot distinguish the hues — nothing is lost, because the colour carries no information the name does not.
- Initial contrast depends on the hashed hue and is not individually verified per hue.
- An avatar used without an accompanying name is silent to assistive technology.
- The bare primitive has no
onError: a picture that fails to load, or that the viewer may not read, leaves the browser's broken-image glyph. Only the app wrappers (PersonPhoto,OrgLogo) recover, falling back to the initials — which is why screens must not call<Avatar photo>directly. - A picture is personal data. It is served same-origin through the API under the record's own read permission (404, never a public URL), and no attribute is derived from it — no face recognition, no matching, no deduplication.
Source: docs/avatar/avatar.a11y.md · GitLab