Activity

Says what the agent is doing now in plain words, and sums up the turn when it ends.

Examples

A turn, from first wait to done

How a turn ends

  • Refunded 3 orders, ran an eval
  • Refunded 3 orders, 1 slow
  • Looked up 2 orders, 1 failed
  • Stopped by Ana Marsh

A finished turn folded to one line

  • Looked up order 4812212ms
  • Read refund-policy.md480ms
  • Sent the receipt to ana@example.commailbox full
  • Retry for Maya Chen to approvedenied

A turn still working

State

  • waiting

  • Searching docs

    activedefault

  • Refunded 3 orders

    done

Status

  • Refunded 3 orders

    okdefault

  • Refunded 3 orders

    warn

  • Looked up 2 orders

    fail

  • Stopped by Ana Marsh

    stopped

Sizes

  • Refunded 3 orders

    defaultdefault

  • Refunded 3 orders

    sm

States

  • waiting

  • Searching docs

    active

  • Refunded 3 orders

    done

  • Looked up 2 orders

    fail

Usage

When to use it
Use
The agent works for a few seconds before any text, and the person needs to know it is alive.
Use
A finished turn folds its tool rows to one line, like "Worked for 14s".
Not for
A thread already shows each tool as a running row. Use Tool call.
Not for
The wait is a determinate task with a percent. Use Progress.
Not for
The model's thinking is the content the person reads. Use Reasoning.

Rules

8 rules
Must
Write label as an -ing verb and its object: "Searching docs", "Running eval". Because the done label is derived from it by pastTense, so "Searching docs" becomes "Searched docs" and a noun phrase does not.
Must
Use waiting only for the gap before the first token, reasoning or tool call of a turn. Because the loader is the first-wait state; once anything arrives the phrase or the thread row takes over.
Should
Pass doneLabel with a summary of the whole turn, and joinSummary for trouble counts. Because the default is the past tense of the last phrase, which tells only about the last step of the turn.
Must
Pass status="warn" or "fail" when the turn warned or failed; stopped when a person ended it. Because the end word and the reserved hue are the only signal that a turn did not go well, and ok hides it.
Must
Pass announce={false} on every Activity inside a Conversation. Because Conversation makes one announcement per turn end, and each Activity with its own region reads the same turn again.
Should
Leave delay, minLoader, minDisplay and settle at their defaults. Because they are tuned so a reply under 150ms never flashes a loader and a phrase stays long enough to read.
Should
Close an ActivityGroup for a finished turn and open it only while the person is reading the rows. Because the summary line already says what happened, and a thread of open groups becomes a list of every call.
Never
Add a spinner, dots or an animated phrase of your own beside Activity. Because the running arc is the one moving shape in the system, and a second one shows two kinds of working.

State order

4 states, highest first
1
waiting
2
active
3
done
4
idle

Accessibility

6 checks
One polite status region reads the loader label and the settled phrase; live phrases are never read, so a burst of tools does not talk over the person.
aria-busy is set while it is waiting or active.
Done is words: warn and fail add their word ("failed", "with a warning") and a status mark, so grayscale and screen readers get it too.
The step glyph is aria-hidden; the row says its state in words.
ActivityGroup is a disclosure button with a 32px row (44px on touch) and a 2px ink focus ring.
Under reduced motion the arc stops and stays a static 3/4 arc.

Keyboard

1 key
Enter, Space on an ActivityGroup
Opens or closes the rows.

Edge cases

10 edge cases
Reply in under 150ms
The loader never shows.
Loader shown, reply lands
It stays at least 500ms, then gives way, so it never flashes for one frame.
Phrases arrive faster than one a second
Each holds for 1000ms and only the newest waiting one shows next; the rest are skipped.
Tool ends and the next starts
It waits 400ms before settling, so the two read as one live phrase.
Phrase is not an -ing verb
pastTense returns it unchanged, so write the phrase with an -ing verb.
Summary already says "stopped"
The end word is not repeated.
No label and no doneLabel
Renders nothing visible, and no region text.
Long phrase
Truncates with an ellipsis in one line.
Right to left
The arc and text mirror; the group chevron flips.
Zoom 200%
The row grows with the text and the phrase truncates.

API

15 props
label
string
active
boolean default false
waiting
boolean default false
doneLabel
string
status
'ok' | 'warn' | 'fail' | 'stopped' default 'ok'
kind
'agent' | 'key' | 'run' | 'deploy' | 'model' default 'agent'
size
'default' | 'sm' default 'default'
minDisplay, settle
number default 1000, 400
delay, minLoader
number default 150, 500
announce
boolean default true
labels
ActivityLabels default English words
ActivityGroup summary, meta, live
ReactNode, ReactNode, boolean default required, -, false
ActivityGroup open, defaultOpen, onOpenChange
boolean, boolean, (open) => void default -, false, -
StepGlyph state
'running' | 'done' | 'fail' | 'stopped' default required
Helpers
pastTense, joinSummary, pickActivity

Tokens

14 tokens
--row-sm
32px
--control-sm
28px
--text-1
var(--ink)
--text-2
oklch(var(--ink-c) / 0.80)
--text-3
oklch(var(--ink-c) / 0.66)
--warn-text
oklch(49% 0.100 68)
--fail-text
oklch(51% 0.180 26)
--surface-hover
oklch(var(--ink-c) / 0.05)
--surface-pressed
oklch(var(--ink-c) / 0.08)
--radius-soft
6px
--type-label
400 14px/16px var(--font-sans)
--type-body-sm
380 13px/18px var(--font-sans)
--dur-fast
120ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.