Skip to content
ArcadiaDesign

TSX

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.

Faces

Six eye styles, each placed by the body so the eyes sit right on a heart, a house or a star.

Picker

Every pick hands back the whole next character; write it at once.

onUpload adds Upload photo (Remove photo once hasPhoto): the product crops and stores the file. Put the picker in a Popover or a small dialog.

Stack

A few agents, overlapping on discs of the surface colour, with the rest folded into a count.

Shape and colour
Size and expression

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, composing and waiting; one hop into done, one droop into failed (on a change, never on mount); idle and asleep hold 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 avatar is 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 +n count.
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.