Handoff: AvatarLabel (agent)
What and why
Activity logs, threads and lists show who did something. For an agent, that is the Agent Avatar next to the agent's name. The PDS AvatarLabel pairs a human Avatar with a label, so this is the same component with the Agent Avatar in place of every Avatar. It also adds an optional attribution ("Zappy on behalf of alex@example.com") and a hasAvatar switch.
The port is a diff against src/components/identity/AvatarLabel on v2.x.
- Figma / Jira: none. Source was the PDS Avatar Label and the design brief for attribution.
- Context:
context.md
Exploration and decisions
- Agent Avatar replaces Avatar everywhere. The human avatar is no longer reachable from this component; for people, use the PDS Avatar Label.
avatarPropsis required (it carries the avatar'slabel), because the visible label is the agent's name and does not say it is an agent.attributionis oneReactNodeholding the whole phrase, not separate "on behalf of" and email props. The connecting words are translatable and word order differs by language, so no text is hardcoded. It renders after the name in the secondary color and truncates before the name does. The screen builds the phrase from the triggering user and passes nothing for scheduled or autonomous runs.hasAvatar(defaulttrue):falseshows only the name and attribution, for when the avatar is already shown nearby. The avatar'slabelstill renders as screen-reader text.- A truncated string attribution keeps its full text in a
title. - The class stays
pds-avatar-label. Open below.
API
| Prop | Type | Default | Purpose |
|---|---|---|---|
ariaLabel |
string |
none | Accessible name for the whole component as a link, if the label is not enough. |
attribution |
ReactNode |
none | Whole phrase saying who the agent acts for. |
avatarProps |
Omit<AgentAvatarProps, 'ariaLabel' | 'linkContent' | 'size'> |
required | Agent Avatar props. label is required. size follows size. |
className |
string |
none | Additional class names. |
hasAvatar |
boolean |
true |
Show the avatar. |
label |
ReactNode |
required | Text next to the avatar. |
linkContent |
ReactNode |
none | Makes the whole component a link. |
size |
'xs' | 's' | 'm' |
's' |
Avatar size and text size. |
Variants and states: three sizes; with and without attribution; with and without the avatar; as a link (hover and focus-visible); truncated attribution.
Tokens used
--pds-border-radius-container--pds-border-width-outline--pds-color-foreground-default--pds-color-foreground-default-secondary--pds-color-interactive-focus--pds-color-interactive-link-default--pds-spacing-2xs--pds-spacing-xs--pds-typography-ff-default--pds-typography-fw-medium--pds-typography-fw-regular--pds-typography-size-s--pds-typography-size-xs
Impact
impact.md finds nothing that reaches existing PDS components. The additions (pds-avatar-label--has-attribution, pds-avatar-label__attribution) only apply where those classes are used. The class name pds-avatar-label is shared with the published package, so this CSS overrides the package's by class name. Nothing else here depends on this proposal. It depends on AgentAvatar.
Reused vs net-new
- Reused: the v2
AvatarLabelstructure, sizes and link behavior; the Agent Avatar proposal. - Net-new: the
attributionandhasAvatarprops, and the two classes above. Seecustom.md.
Accessibility
- The avatar's
labelalways reaches screen readers, even withhasAvatar={false}(as visually-hidden text, out of flow). - A plain space is rendered between the name and the attribution so they are not read as one word.
- As a link, the whole component is the link; focus-visible uses
--pds-color-interactive-focus. - The a11y panel has not been run on these stories. Run it in light and dark before the port.
Decisions and open questions
- Rename the class (for example
pds-agent-avatar-label), since the published package already ships CSS forpds-avatar-label? - Should the email in the attribution be a separate element so it can be copied or linked?
attributionis a working name; alternatives wereonBehalfOfandactingFor.
Port notes
- Goes in
src/components/identity/AvatarLabel(or a newAgentAvatarLabelnext to it, depending on the class-name call above). Usenpm run generate:component. - Swap the local
mergeClassesfor the real one. Port AgentAvatar first.
Preview
npm run storybook, then Component Proposals/Identity/Avatar Label.
Figma: https://www.figma.com/design/AMGWJbraJoi2PKWnxo29Ah/PDS-Proposals?node-id=374-43, page Avatar Label (6 stories, light only; captures of Storybook with library icons swapped in, sent 2026-10-20).