Agent avatar
A Lookout agent's character: a soft body, a colour and two eyes drawn from a seed, with a picker and a stack.
import { AgentAvatar, AgentAvatarPicker, AvatarStack and 1 more } from "@arcadia/ui"A character is a body, a colour, a face and a small tilt, drawn from a seed. Store the picked fields, not the seed: a later change to the lists never re-skins an agent. characterFromId draws the same character for the same id every time; shuffledCharacter draws a fresh one, and both can avoid one colour so two agents made in a row never look alike.
At 20px and below the body is drawn flat with dot eyes, so the silhouette stays legible in a row. A photo shows circle-cropped at every size, with the character standing in while it loads and if it fails.
Examples
Sizes
16 to 96. A ListRow leading slot takes 20 and a person row 36; a pinned tile takes 56, the profile header 72 and the empty state 96.
Expressions
Eyes only, no mouth: working reads along a line, waiting widens, sleepy closes, sad drops the inner corners and happy curves into two arcs.
A state picks its own; expression overrides it.
States
state gives the character a body: it rests still, sways while it thinks, leans into its work, bobs while it writes, nods while it waits for you and lies still asleep.
Every loop multiplies or divides the one clock. At 20px and below only the eyes tell the state, and a photo stays still.
A turn ends
Into done the character hops once and its eyes smile; into failed it droops and holds the pose.
Both play when the state changes, never on mount. interactive widens the eyes and leans the body toward the pointer; poke wobbles it on a click, where a click means nothing else.
Badge
badge puts a state mark on the corner, on a disc of the surface (--agent-avatar-badge-ring), so it reads over any body colour. It pops in when it appears.
rootStyle takes layout, the blink period and the badge's ring. The stack takes the ring colour of the surface it sits on.
--agent-avatar-blink- `calc(${Ref<Duration>} * ${number})`Default calc(var(--loop-watch) * 1.6)
- One blink cycle (seeded per agent from 1.25× to 2.2× the watch loop, about 4 to 7 seconds).
--agent-avatar-badge-ring- PaintDefault var(--bg-app)
- The disc the corner badge sits on: the surface the avatar stands on, so the dot reads over any body colour.
--avatar-stack-ring- PaintDefault var(--bg-sidebar)
- The ring around each avatar: the surface the stack sits on.
AgentAvatar is an img role named by name; the drawing inside is decorative. Where the name is written beside it, as in the rows above, wrap it in an element with aria-hidden in dense lists.
AvatarStack is one image named by its label followed by every agent's name, the folded ones included. Each picker control is a toggle button with aria-pressed and a name ("Heart", "Blue", "Tilted eyes"), in labelled groups.
The state is never told by motion alone: the eyes and the badge carry it, and the text beside the avatar says it in words. Every clip, the blink and the working glance stop under reduced motion, and a character scrolled out of view pauses. The badge is decorative; say the state in the row's own text.
AgentAvatar
avatarrequired- AgentAvatarValue
- The stored avatar: a character, or a photo with its character as the stand-in.
namerequired- string
- Accessible name: the agent's name.
size- AgentAvatarSizeDefault 20
- Width and height in pixels. 20 and below draw flat with dot eyes, so the shape stays legible.
state- "idle" | "thinking" | "working" | "composing" | "done" | "failed" | "waiting" | "asleep"Default "idle"
- What the agent is doing: the body moves in a clip and the eyes take the state's mood. Loops for
thinking,working,composingandwaiting; one hop intodone, one droop intofailed(on a change, never on mount);idleandasleephold still. Sizes 20 and below keep the eyes only; a photo stays still. expression- AgentExpression
- Eye-only mood, when it should differ from the state's:
working,waiting,sleepy,sad,happy. Default: the state's. live- boolean
- Blinks while composing or waiting (live agents, and every avatar at 72px and up). Never under reduced motion.
breathe- boolean
badge- Exclude<StatusDotStatus, "idle" | "draft">
- A state mark on the corner, on a disc of the surface:
running,unread,attention,error. It pops in. interactive- boolean
- Under the pointer the eyes widen and the body leans toward it. For an avatar that is itself the thing hovered.
poke- boolean
- A click wobbles the character (a full turn just after
done). Only where a click means nothing else. enter- boolean
- Pops in once on mount: scale and fade on an overshoot. Never under reduced motion or on a hydrated page.
photoSrc- string
- The photo's address when
avataris a photo; while it loads, and if it fails, the character shows. rootStyle- RootStyle<AgentAvatarKnobs>
- Typed StyleX override: layout, the blink knob and the badge's ring.
Rest props go to HTMLAttributes<HTMLSpanElement>.
valuerequired- CharacterAvatar
- The character as it is now.
onChangerequired- void
- A shape, colour or face was picked: the whole next character (write it at once; the picker holds no state).
onShuffle- void
- Shuffle: a new random character. Omit to hide the button.
onReset- void
- Reset: back to the character the agent was created with. Omit to hide the button.
onUpload- void
- A picture was chosen (png, jpg or webp): the product crops and stores it. Omit to hide "Upload photo".
hasPhoto- boolean
- The agent shows a photo now: offers Remove photo (with
onRemovePhoto). onRemovePhoto- void
- Remove the photo: the character shows again.
namerequired- string
- The agent's name, for the controls' accessible names.
rootStyle- RootStyle
- Typed StyleX override: where the picker sits.
Rest props go to HTMLAttributes<HTMLDivElement>.
agentsrequired- AvatarStackItem[]
- The agents, in order; the first sits on top.
size- 16 | 20 | 24Default 16
- Each avatar's size.
max- numberDefault 3
- At most this many avatars; the rest fold into a
+ncount. label- string
- Accessible name for the whole stack ("Agents on lookout"); the names follow it.
rootStyle- RootStyle<AvatarStackKnobs>
- Typed StyleX override: layout and the ring knob.
Rest props go to HTMLAttributes<HTMLSpanElement>.
shaperequired- AgentShape
colorrequired- AgentColor
facerequired- AgentFace
tilt- numberDefault 0
expression- AgentExpressionDefault "neutral"
simple- booleanDefault false
- One flat fill, dot eyes: legible at 20px and below.
gradientIdrequired- string
- Unique per instance: the body's gradient id.
eyes- SVGProps<SVGSVGElement>
- Attributes for the eye pair (AgentAvatar's motion class).
Rest props go to SVG element props.