Avatar component
components specs/components/avatar.kmd
The visual representation of a person or entity — photo with initials and generic-icon fallbacks, a size scale, optional presence indicator — plus the overlapping avatar group (face pile) with a "+N" overflow. Modeled after Atlassian Avatar / AvatarGroup. Used by comments, assignees, timelines, member lists, and topbars across Koder.
When this spec applies
Primary triggers
- Render an avatar or an overlapping avatar group
All triggers
- Show a user's photo / initials avatar
- Show a group of people overlapping (assignees, viewers)
- Indicate online/away presence on an avatar
Specification body
Component — Avatar
Status: v0.1.0 — Draft. Promoted from the Atlassian Design parity scan (meta/docs/stack #088). Source: https://atlassian.design/components/avatar/ + https://atlassian.design/components/avatar-group/
R1 — Content fallback chain
A single avatar resolves its content in strict priority:
- photo (when available + loaded);
- initials — 1–2 letters derived from the entity name (R5);
- generic icon — a neutral person/entity glyph when no name.
A broken/slow image falls back to initials, never a broken-image box.
R2 — Shape & size
- Circle for people; rounded-square for non-person entities (teams, orgs, apps) — shape disambiguates kind.
- Size is a token scale (
xs/sm/md/lg/xl); initials and any presence dot scale with it. No arbitrary pixel sizes.
R3 — Presence indicator (optional)
- A small status dot/ring (online / away / busy / offline) may overlay a
corner. Status is conveyed by icon/shape + position, not color alone
(WCAG 1.4.1); the dot reuses
specs/components/badges.kmdgeometry.
R4 — Deterministic initials background
The initials background color is derived deterministically from the entity id/name (stable per identity, identical across surfaces and renders) — never random per render. The palette comes from Verge tokens and meets text-contrast on the fill.
R5 — Avatar group (overlapping / face pile)
- N avatars overlap left-to-right by a fixed token offset; stacking order is stable (deterministic, not render-order).
- Beyond a
max, collapse the remainder into a trailing "+N" counter avatar; activating it opens a list (specs/components/lists.kmd) / popover of the full set. - The group keeps a single, predictable reading order.
R6 — Accessibility
- Each avatar has an accessible name = the entity's name; when the name is
already adjacent (a comment header), the avatar image is decorative
(
alt=""). - An avatar group announces its membership and overflow: "5 people: Ana, Bruno, Carla and 2 more".
- The "+N" control is a real button (keyboard-focusable) that reveals the rest.
R7 — i18n
- Initials derivation is locale-aware (handles multi-byte names, RTL, mononyms); never assumes "first letter of first + last word" universally.
- "+N" / "N more" uses locale number formatting + ICU plural per
specs/i18n/contract.kmd.
Não-escopo
- Profile cards / hovercards (a separate surface that may contain an avatar).
- Identity/auth (
specs/auth/oauth-flow.kmd); avatar is presentation only. - Image upload/cropping (a product flow).
Requirements (testable)
Requirement: Content fallback chain {#req-avatar-fallback-chain}
A single avatar SHALL resolve its displayed content in strict priority order — loaded photo first, then 1–2 initials derived from the entity name, then a neutral generic person/entity glyph when no name is available — and SHALL NOT render a broken-image box when the image is broken or slow to load.
Scenario: Photo available and loaded
- GIVEN an avatar configured with an entity name and a photo URL that loads successfully
- WHEN the avatar renders
- THEN it displays the photo image
- AND it does not display initials or the generic glyph
Scenario: Image broken falls back to initials
- GIVEN an avatar configured with the entity name "Ana Lima" and a photo URL that fails to load
- WHEN the image load errors
- THEN the avatar displays the initials "AL"
- AND no broken-image box is shown
Scenario: No name falls back to generic glyph
- GIVEN an avatar with no photo and no entity name
- WHEN the avatar renders
- THEN it displays the neutral generic person/entity glyph
- AND it does not display any initials
Requirement: Shape and size scale {#req-avatar-shape-size}
An avatar SHALL use a circle for people and a rounded-square for non-person entities (teams, orgs, apps) so that shape disambiguates kind, and its size SHALL come from the token scale (xs/sm/md/lg/xl) with initials and presence dot scaling accordingly, never an arbitrary pixel size.
Scenario: Person renders as circle
- GIVEN an avatar whose entity kind is "person"
- WHEN the avatar renders
- THEN its shape is a circle
Scenario: Non-person renders as rounded-square
- GIVEN an avatar whose entity kind is "team"
- WHEN the avatar renders
- THEN its shape is a rounded-square
Scenario: Size resolves from token scale
- GIVEN an avatar with size token
lg - WHEN the avatar renders
- THEN its rendered dimensions equal the
lgtoken value - AND any presence dot is scaled to the
lgtoken
Requirement: Presence indicator is not color-only {#req-avatar-presence}
When an avatar shows an optional presence indicator, the online/away/busy/offline status SHALL be conveyed by icon/shape plus corner position and SHALL NOT rely on color alone (WCAG 1.4.1), reusing the badge geometry from specs/components/badges.kmd.
Scenario: Status distinguishable without color
- GIVEN an avatar with a presence indicator set to "busy"
- WHEN the avatar renders with color removed (grayscale)
- THEN the status remains distinguishable from "online" via its icon/shape or position
- AND the presence dot occupies a fixed corner position
Requirement: Deterministic initials background {#req-avatar-initials-bg}
The initials background color SHALL be derived deterministically from the entity id/name so it is stable per identity and identical across surfaces and renders, SHALL NOT be random per render, and SHALL be drawn from Verge tokens meeting text-contrast on the fill.
Scenario: Same identity yields same background across renders
- GIVEN two avatars for the same entity id "user-42" with no photo
- WHEN both render on different surfaces
- THEN both use the identical initials background color
- AND that color is a Verge palette token
Scenario: Background meets text contrast
- GIVEN an avatar showing initials on its deterministic background
- WHEN contrast between the initials text and the fill is measured
- THEN it meets the required text-contrast threshold
Requirement: Avatar group overflow {#req-avatar-group-overflow}
An avatar group SHALL overlap its members left-to-right by a fixed token offset with a stable deterministic stacking order, and beyond the configured max SHALL collapse the remainder into a trailing "+N" counter avatar whose activation opens a list/popover of the full set.
Scenario: Overflow collapses into +N counter
- GIVEN an avatar group of 7 members with
maxset to 4 - WHEN the group renders
- THEN 4 avatars are shown followed by a trailing counter reading "+3"
- AND the members overlap left-to-right by the token offset
Scenario: Activating +N reveals the full set
- GIVEN a rendered avatar group showing a "+3" counter
- WHEN the "+3" counter is activated
- THEN a list/popover opens containing all 7 members
Requirement: Accessibility and keyboard {#req-avatar-a11y}
Each avatar SHALL expose an accessible name equal to the entity's name (or be decorative with alt="" when the name is already adjacent), an avatar group SHALL announce its membership and overflow count, and the "+N" control SHALL be a real keyboard-focusable button that reveals the rest.
Scenario: Avatar exposes accessible name
- GIVEN a standalone avatar for the entity "Bruno Souza" with no adjacent name label
- WHEN assistive technology inspects the avatar
- THEN its accessible name is "Bruno Souza"
Scenario: +N is a keyboard-operable button
- GIVEN an avatar group with a "+2" overflow control
- WHEN the user tabs to the control and activates it via keyboard
- THEN the control receives focus as a button
- AND activation reveals the remaining members
References
specs/components/badges.kmdspecs/components/lists.kmdspecs/app-layout/topbar-placement.kmdspecs/i18n/contract.kmd