Status: draft M0. Specification of the core components for packages/design_system. Sizes and
colors only as token names; numbers in tokens.md and palette.md. Every
text or icon color on a surface is listed as a pairing in design/design-rules.json and checked in
contrast.md for light and dark. Images:
Components dark, light;
Terminal dark, light; Statistics
dark, light.
Data fields according to 03-data-model.md, events according to
04-sync-protocol.md.
Hit area at least density.target-min, even when the visible area is smaller.
Semantics: Every component sets role and label for screen readers (Flutter Semantics,
on the web as ARIA). Labels come from packages/l10n, never from the agent's data alone.
Never color alone: States differ in icon shape and text.
Motion only through motion.*; no motion in the reduced context.
Accent roles from DsAccent: components read color.accent.*, color.text.link,
color.focus.ring and color.run.running only from the theme extension DsAccent (Dart) or the
--ds-* variables under data-accent (web), never from DsColors, so the instance accent
(branding.accent_color) applies everywhere. Every variant is in contrast.md.
Purpose: a post in a thread (messages), from a person, agent, app, import or system.
Anatomy: avatar (person round, agent rounded square radius.md, app with a small badge),
header (name, for agents the badge "AGENT", timestamp, for triggering messages the
trigger state), content (Markdown), attachments, components (for kind = card), reactions,
reply reference (quote line above the content), hover bar (Reply, React, Edit,
Pin, More).
Variant
Data
Rendering
Text
kind = text
Markdown in type.body or density.message-font-size
Continuation message
same author within ui.message_group_window
without avatar and header; timestamp on hover
Voice message
kind = voice
play button, duration, progress; transcript below as soon as transcript_status = done
Live box anchor
kind = run
renders the live box (section 2) at this position
Card
kind = card
content plus components (section 8) in color.surface.raised
System
kind = system
one line in type.caption, color.text.secondary, icon on the left
Mentions me
me in mentions
tinted background color.attention.subtle, spine color.attention.border with border-width.spine
sending
entry in the outbox
reduced opacity, line "Sending" with the trigger.queued icon
failed
outbox, permanent error
line in color.danger.fg with the reason, buttons "Resend", "Edit", "Discard"
edited
edited_at set
suffix "(edited)" in color.text.muted; versions via message.revisions.read
deleted
deleted_at set
line "Message deleted" in color.text.muted; no hover bar
Long replies: content over ui.message_collapse_lines lines is collapsed; a text button
"Show more" (color.text.link) expands it. Code blocks in type.code on color.code.bg,
horizontally scrollable, with a copy button.
Sizes: avatar size.avatar-md; gap from avatar to content space.12; group gap
density.message-gap; row padding density.message-pad-y; reading width at most
layout.message-max-width.
Accessibility: A message is a semantics node with the label ",
Purpose: display of a run (runs) with state, metrics, status line, steps and
last text. It is pinned at the top of the thread while the run is active, and at the end it
collapses into one line.
Data: run document run_document.v1 (04, section 11): state, version, status_line,
last_text_excerpt, metrics (context_pct, tokens, subagents, background jobs,
API retries), cost, billing_mode_used, steps, followups. State changes arrive as the
durable run.state_changed, content as the ephemeral run.updated (only with focus).
Header: state icon and state word (run.*), run time, context in percent,
tokens, cost (all in type.code-sm), backend policy badge (section 10),
expand arrow.
Status line (section 4).
Steps: tool chips (section 3) in ordinal order, substeps indented
(parent_step_id). The last ui.livebox_visible_steps are visible; older ones are behind
"n earlier steps".
Last text:last_text_excerpt as a quote with a line on the left in color.border.default.
Footer: button "Stop" (variant danger-outline, icon action.stop), note about follow-ups.
For waiting_input, the form or the buttons of the question (section 8) instead of the note.
Stall: After run.stall_after without progress, a banner appears in
color.warning.* ("No progress for n minutes").
Surface color.surface.raised, border color.border.default, radius radius.md, padding
density.livebox-pad. Spine on the left in border-width.spine with the color of the state:
State
Spine and icon
Icon
Word
queued
color.run.queued
run.queued
"Queued"
running
color.run.running
run.running (spins)
"Running"
waiting_input
color.run.waiting
run.waiting
"Waiting for you" or "Waiting for Alex"
completed
color.run.completed
run.completed
"Done"
failed
color.run.failed
run.failed
"Failed" plus reason
cancelled
color.run.cancelled
run.cancelled
"Canceled"
Reasons (state_reason) are translated, never shown raw: billing_mode_mismatch ->
"Backend does not match", access_revoked -> "Access revoked", stale -> "Canceled after
stall", user_cancelled -> "Canceled by ", adapter_error -> "Error in the adapter",
cancel_timeout -> "Adapter did not confirm the cancellation", project_not_ready -> "Project
not ready". Unknown reasons: "Failed" plus the code in type.code-sm.
One line on color.surface.sunken, radius radius.md: icon, "Ran ", number of
tools, subagents, cost with "Estimate" as long as cost_is_estimate. The summary
comes from run.state_changed; expanding loads the document via REST (run.read). Study B
proposed the format "Ran 28 min · 3 agents · USD 1.20"; it is adopted.
Compact line below the navigation bar (height density.control-height plus padding):
state icon, state and run time, status line truncated to one line, stop button on the right with
hit area density.target-min. Tapping opens the full live box as a sheet. As built (M5-02): state
icon, state and run time wrap and the status line sits below them, so text scaling up to 200
percent loses nothing; the stop button stays at the top right of the line.
The live box is a semantics container with the label "Run of : , for ,
context percent".
Live region: only state changes and new questions are announced (polite), at most once
per a11y.live_announce_min_interval. Patches to steps and numbers are never announced; they
would flood screen readers.
The stop button has the label "Stop run of " and is reachable by a shortcut
(01-product.md, Keyboard shortcuts).
Anatomy: icon by kind (step.*), name (name, for example the tool name) in type.code-sm,
short description (title) in color.text.secondary, duration or "running n s". Surface
color.surface.sunken, radius radius.sm, height density.row-channel minus padding.
status
Icon color
Suffix
pending
color.run.queued
"waiting"
running
color.run.running, icon spins
run time
completed
color.run.completed
duration
failed
color.run.failed
"failed", detail on expand
cancelled
color.run.cancelled
"canceled"
Click or Enter opens detail (run_step_detail.v1) in a disclosure; contents are truncated
to run.step_detail_max_bytes. Label: ", , ".
One sentence on what the agent is doing right now (runs.status_line), in type.body-sm,
color.text.primary, one line, truncated at the end with a tooltip. Inline code allowed. Empty:
the line is omitted. Not part of the live region (see 2.4).
Purpose: state on the triggering message of a person (messages.trigger_state); replaces
the status emoji from Discord.
Placement: in the header of the message after the timestamp, size icon.size.sm. Tapping or
hovering shows the caption and jumps to the live box.
trigger_state
Icon
Color
Label (DE)
Label (EN)
received
trigger.received
color.trigger.received
Empfangen
Received
queued
trigger.queued
color.trigger.queued
In Warteschlange
Queued
working
trigger.working (spins)
color.trigger.working
Wird bearbeitet
Working
done
trigger.done
color.trigger.done
Erledigt
Done
failed
trigger.failed
color.trigger.failed
Fehlgeschlagen
Failed
cancelled
run.cancelled
color.run.cancelled
Abgebrochen
Canceled
followup_delivered
trigger.followup
color.trigger.followup
Nachtrag zugestellt
Follow-up delivered
For failed, the reason is in the tooltip and in the semantics label ("Failed: Project not
ready"). Messages without a trigger (trigger_state empty) show nothing. cancelled is a deliberate stop
(09 section 6): it uses the neutral run color, never the error style of failed, and names the
reason the same way ("Canceled: Canceled by Sam").
Purpose: dense list of the posts of a forum, sorted by last activity
(threads.last_activity_at) or by creation (forum_default_sort).
Row (height density.row-post, two lines):
on the left a state icon: active run (run.* from "Running now"), otherwise mention
(color.attention.fg), otherwise nothing;
title in type.body-sm, bold when unread; time on the right in type.caption;
second line: badge "Waiting for you" (color.attention.bg), operator agent,
mention counter, tags (category.<name>.chip-*), on error the reason in color.danger.fg.
States of the row: rest, hover (color.surface.hover), selected (color.surface.selected),
focus. Header: forum name, button "New post" (primary), filter chips (Open, Waiting for me,
Tags, Sort). At the bottom the done area: row "Done (n)", collapsed by default;
when expanded, done posts follow in color.text.muted.
Accessibility: list with the role "list", rows with the label "