Skip to content
Dark

AI

Two components, Message and Prompt composer. Between them they cover a thread, an answer that is still arriving, an answer that failed, and the five states of the input underneath. Both are mobile width and both read from the same tokens as everything else.

Preview

Two surfaces an AI product cannot borrow from anything else in the system: the conversation row and the prompt input. Everything standing around them, suggestion chips, source cards, the tools menu and the model picker, is Chip, Card, Context Menu and Dropdown.

Message Role User, Message Role Assistant, Prompt Composer State Empty
all three 361 wide, 72, 96 and 88 tall
Summarise the Q3 report and pull out anything about churn.
Churn rose to 4.1 percent in Q3, up from 3.2 in Q2. Almost all of it sits in month two of the trial. The annual plan is flat, so the problem is the monthly cohort, not the product.
Ask anything

The two sets never meet inside the file. A thread is a column of Message rows and the composer is pinned under it, so neither knows what the other holds. The exchange above is the copy the file's own example screen carries, not the component defaults.

Message

One conversation row. Role decides the shape, State decides what an unfinished answer looks like. Both are on the same set, so a thread is one component repeated.

Role User and Role Assistant, both State Default
361 x 72 and 361 x 48, bubble 280 at radii 20 20 6 20
Churn rose to 4.1 percent in Q3, up from 3.2 in Q2.
Churn rose to 4.1 percent in Q3, up from 3.2 in Q2.
State Streaming, 361 x 66
Churn rose to 4.1 percent in Q3, up from 3.2 in Q2.
State Error, 361 x 112
The request timed out before a full answer came back.
Try again
Property
Values
What it changes
Role
User, Assistant
User draws a 280 bubble in bg/soft-100 pushed right. Assistant draws no container and runs the full 361.
State
Default, Streaming, Error
Streaming hangs a 28 x 6 row of three dots under the answer. Error swaps it for a 12 radius panel with an icon, a reason and a retry.
Message
Text
The body copy, and the only text property the set exposes.

The grid is deliberately sparse: 4 of the 6 Role by State cells exist, because a user turn cannot stream and cannot fail. Two things do not line up. The Answer frame uses gap 8 in Default and gap 12 in Streaming and Error, and the Actions frame under the error carries a hardcoded 30 of left padding to line the button up with the copy rather than with the icon.

Prompt composer

The input everything else is arranged around. One shell, five states, and the state is the surface being honest about what it is doing: waiting, ready, working, carrying a file, or shut.

State Empty
361 x 88, radius 24, 1px inside stroke, dropShadow/sm
Ask anything
Typing, 361 x 112. Send turns brand/base as soon as the field is not empty
Compare churn against the same quarter last year
Generating, 361 x 112. Send is replaced by Stop on the same disc
Compare churn against the same quarter last year
Disabled, 361 x 112. Offline and over limit, every glyph fg/disabled-400
Compare churn against the same quarter last year
Attachment, 361 x 175. The file sits above the prompt with its own remove
Board pack Q3.pdf
PDF, 2.4 MB
Compare churn against the same quarter last year

Empty still draws the send button, dormant in fg/disabled-400, rather than hiding it. The plus opens the tools menu, a Context Menu instance, so the tool list is not fixed here. Padding is not symmetric: 12 top and left, 10 right and bottom, which centres a 32 disc in the corner.

Anatomy

Measured in the file, not rounded to a scale. Where the file sits off its own spacing scale it is written here as it stands.

Part
Measured
How it resolves
User bubble
280 x 72, padding 12 and 16, radii 20 20 6 20
Fixed 280 in a 361 row aligned right. 16 + 248 + 16 = 280, and the clipped corner is the near one.
Assistant answer
361 wide, 48 at two lines
No frame, no fill. The Answer stack is gap 8 in Default and gap 12 in Streaming and Error.
Error panel
361 x 112, padding 12, radius 12
Row 337 x 40 at gap 10, then a 100 x 36 button. Actions carries 30 of left padding, off the scale, to align the button with the copy.
Composer shell
361 wide, padding 12 10 10 12, radius 24, gap 10
Content is 339 in every state. Heights run 88, 112, 112, 112 and 175.
Controls row
339 x 32, gap 8, tools gap 6
Discs are 32 at radius 999 holding an 18 glyph. Stop is an 11 x 11 rectangle at radius 2.5.
Attachment row
339 x 53, padding 8 10 8 8, radius 14
Tile 36 at radius 10, remove 20 at radius 10, meta gap 1. 8 + 36 + 10 + 245 + 10 + 20 + 10 = 339.

Both 1px strokes are drawn INSIDE, so a CSS border would make the shell 363 and the attachment row 55. They are reproduced as inset shadows, which add nothing.

Tokens

Nothing on either component carries a raw colour. Values are the light resolution; each has a dark counterpart inside the same variable.

Where
Token
Light
Composer surface
bg/white-0
#FFFFFF
Bubble, discs, tile
bg/soft-100
#E9E9EA
Disabled shell, retry
bg/weak-50
#F4F5F5
Remove control
bg/sub-200
#D1D1D6
Both 1px strokes
border/soft-100
#E9E9EA
Answer, bubble, file name, x
text/strong-950, fg/strong-950
#0E0E0F
Retry label, plus glyph
text/sub-700, fg/sub-700
#48484A
Placeholder, meta, dots
text/soft-500, fg/soft-500
#8E8E93
Dormant and disabled glyphs
fg/disabled-400
#AEAEB2
Send and Stop disc
brand/base
#155DFC
Glyph on that disc
text/on-color/strong
#FFFFFF
Error panel, icon, copy
state/error/lighter-50, base-500, dark-950
#FEF2F2 #FB2C36 #460809

In dark mode bg/white-0 becomes #0E0E0F and text/strong-950 becomes #FFFFFF, so surface and copy swap ends of the ramp together. The error tint is the one that does not simply invert: #FEF2F2 becomes red at 16 percent alpha.

Guidelines

One rule the set already decided for you, and two it cannot enforce.

Do
Churn rose to 4.1 percent in Q3.
Churn rose to 4.1 percent in Q3.

Let the assistant run as plain text on the full 361. The reader scans one column and a long answer is not squeezed into a 280 bubble.

Don't
Churn rose to 4.1 percent in Q3.
Churn rose to 4.1 percent in Q3.

Do not give the assistant a bubble as well. Two bubbles read as two people, and the answer loses the width it was built for.

The set carries no Streaming and no Error on the User side, so a failed send is not a Message state: handle it on the composer or as a Toast. Keep Generating reachable, because Stop is the only control on either component that ends a running answer. And the composer never grows on its own: its height follows the value, so decide the ceiling yourself.