Handoff: AgentAvatar
What and why
People and agents show up side by side in activity logs, comment threads and lists. A user has to know at a glance when an agent did something, and the PDS Avatar looks like a person. This proposal is a purpose-built agent avatar: a dark disc with a lightning bolt, ringed by the AI gradient. The ring is the first cue and the bolt is the second, so the signal never relies on color alone, and a required screen-reader label says it is an agent.
It started from the PDS v2 Avatar and keeps only its sizes, link behavior and a11y. The image, per-user color and user fallback are removed.
- Figma: Agent Avatar page in PDS-Proposals (6 stories, light and dark, flat captures; sent 2026-10-16). Jira: none. Source was Erika's brief and a reference screenshot.
- Context:
context.md
Exploration and decisions
- A separate component, not an
Avatarvariant. One shared look for all agents. Per-agent identity is the name shown next to it. - The ring is stacked backgrounds.
--pds-gradient-ai-activeis a gradient, so it cannot go inborder-color. The fill sits on the content-box, a surface-colored gap on the padding-box and the gradient on the border-box. - Fill and icon use the foreground tokens (
--pds-color-foreground-defaultand-reverse). Both flip in dark mode, so it becomes a light disc with a dark bolt. hasHalo(defaulttrue). Off leaves the disc and bolt at the same overall size; the icon is not enlarged.shape(circledefault,square) was added back on request after the non-agentic options were removed.iconName:zappyfor Pantheon agents,sparklesSolidfor third-party agents.
API
| Prop | Type | Default | Purpose |
|---|---|---|---|
ariaLabel |
string |
none | Accessible name when used as a link. Falls back to label. |
className |
string |
none | Additional class names. |
hasHalo |
boolean |
true |
Show the AI-gradient ring around the avatar. |
iconName |
'sparklesSolid' | 'zappy' |
'zappy' |
Icon inside the avatar. |
label |
string |
required | Screen-reader text that says this is an agent. Pass it translated. |
linkContent |
ReactNode |
none | A link element from the router of your choice. |
shape |
'circle' | 'square' |
'circle' |
Avatar shape. |
size |
'xs' | 's' | 'm' | 'l' | 'xl' |
's' |
16px, 20px, 24px, 32px, 40px. |
Variants and states: circle and square; halo on and off; the five sizes; as a link (hover and focus-visible).
Tokens used
--pds-border-radius-default--pds-border-width-outline--pds-color-foreground-default--pds-color-foreground-reverse--pds-color-interactive-focus--pds-color-surface-default--pds-gradient-ai-active
Impact
impact.md finds nothing that reaches existing PDS components: everything is scoped to pds-agent-avatar. It does not list the proposals that build on this one, though: AvatarLabel and IdentityBlock render it, and Spinner's hasAvatar uses it. Port it first.
Reused vs net-new
- Reused: PDS
Icon(through the local wrapper, forsparklesSolid), design tokens, the v2Avatarstructure and link behavior. - Net-new: the whole component. See
custom.md(classpds-agent-avatar, five--avatar-*variables, no raw values).
Accessibility
- The avatar is decorative to the eye and named for screen readers:
labelrenders asvisually-hiddentext, and the icon isaria-hidden. - As a link, the link's
aria-labelisariaLabel, falling back tolabel. Focus-visible uses--pds-color-interactive-focus. - The agent signal is the ring and the bolt together, so it does not rely on color alone.
- The a11y panel has not been run on these stories. Run it in light and dark before the port.
Decisions and open questions
- Confirm in the browser that the 1px ring and gap read at
xs(2x), and that the dark-mode inversion (light disc, dark bolt) is wanted. - Should the icon grow when
hasHalois off? - No white-on-gradient token exists; the fill and icon use the foreground tokens.
sparklesSolidis a local icon added by the Icon proposal (see../Icon/). Port that with this; the Icon proposal is ready (../Icon/HANDOFF.md).
Port notes
- Goes in
src/components/identity/AgentAvatarinpds-toolkit-react, next toAvatar. Create it withnpm run generate:component, then copy the files over. - Swap the local
mergeClassesfor the real one, and the localIconwrapper for the packageIcononcesparklesSolidexists there. - Known gaps against v2 conventions: none beyond the above.
Preview
npm run storybook, then Identity/Agent Avatar.