Skip to content

AI

Message is one row of a conversation, and Prompt composer is the input under it. Together they cover a thread, an answer that is still arriving, an answer that failed, and the five states of the input.

On this page
  1. Preview
  2. Message
  3. Prompt composer
  4. Anatomy
  5. Tokens
  6. Two composers
  7. iOS and Material 3
  8. Conversation layout

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 Emptythread 361 wide, composer 361 x 96 at radius 32
Summarize 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.

Source: the AI / Message and AI / Prompt Composer sets of the Appetite UI Figma file, read on October 2, 2026.

A thread is a column of Message rows and the composer is pinned under it, so neither component knows what the other holds. The exchange above is sample copy from the example screen, in use on the AI screens.

The three dots under a streaming answer are drawn inside Message. For an indicator that stands on its own, use Typing Indicator on the Progress page.

Which roles and states does Message have?

Message has two Roles, User and Assistant, and three States, Default, Streaming and Error. Role decides the shape and State decides what an unfinished answer looks like, so a thread is one component repeated.

Role User and Role Assistant, both State Default361 x 48 and 361 x 24, 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 42

Churn rose to 4.1 percent in Q3.

State Error, 361 x 112

The request timed out before a full answer came back.Try again
Message, property, values, what it changes
PropertyValuesWhat it changes
RoleUser, AssistantUser draws a 280 bubble in bg/soft-100 pushed right. Assistant draws no container and runs the full 361.
StateDefault, Streaming, ErrorStreaming hangs a 28 x 6 row of three dots under the answer. Error swaps the answer for a 12 radius panel with an icon, a reason and a Retry button.
MessageTextThe body copy, and the only text property the set exposes. It defaults to Message text.

Four of the 6 Role by State cells exist, because a user turn cannot stream or fail. Handle a failed send on the composer or as a Toast.

The Retry button is a Button at Type Error, Style Weak and Size sm: a state/error/base-500 label on a red tint at 10 percent.

The Answer frame uses gap 8 in Default and gap 12 in Streaming and Error, and the Actions frame under the error carries 30 of left padding to line the button up with the copy.

Which states does the Prompt composer have?

The Prompt composer has five states: Empty, Typing, Generating, Disabled and Attachment. One shell carries all five, and the state says what the input is doing: waiting, ready, working, shut or carrying a file.

State Empty361 x 96, radius 32, padding 16, 1px inside stroke, dropShadow/sm

Typing, 361 x 96

Compare churn against last year

Generating, 361 x 96

Ask anything

Disabled, 361 x 96

Ask anything

Attachment, 361 x 157

Board pack Q3.pdfPDF, 2.4 MBCompare churn against last year

Empty draws the send disc dormant, bg/soft-100 with an fg/disabled-400 arrow. Typing turns the disc brand/base as soon as the field holds text.

Generating clears the field, because the prompt has been sent, and puts Stop on the same disc. Disabled covers offline and over the limit, every glyph on fg/disabled-400 over a bg/weak-50 shell.

Attachment puts the file above the prompt with its own remove control. The plus opens the tools menu, a Context Menu instance, so the tool list is not fixed.

The shell is padded 16 on every side at radius 32, the same 16 as platform/screen-padding in the Platform collection. It is 96 tall at one line and 157 with a file and grows with its value, so set the ceiling in code.

Anatomy

Every part is measured and written as drawn. Where a value sits off the spacing scale it is written as it stands.

Anatomy, part, measured, how it resolves
PartMeasuredHow it resolves
User bubble280 wide, padding 12 and 16, radii 20 20 6 20, 48 tall at one lineFixed 280 in a 361 row aligned right. 16 + 248 + 16 = 280, and the clipped corner is the near one.
Assistant answer361 wide, 24 tall at one lineNo frame, no fill. The Answer stack is gap 8 in Default and gap 12 in Streaming and Error.
Streaming dots28 x 6, three dots of 6 at gap 5fg/soft-500, 12 under the text.
Error panel361 x 112, padding 12, radius 12Row 337 x 40 at gap 10 with a 20 triangle-alert, then a 100 x 36 Retry. Actions carries 30 of left padding, off the scale, to align the button with the copy.
Composer shell361 wide, padding 16, radius 32, gap 8Content is 329 in every state. Heights run 96 and 157 with a file.
Controls row329 x 32, gap 8, tools gap 6Discs are 32 at radius 9999 holding an 18 glyph at 1.75. Stop is an 11 square at radius 2.5.
Attachment row329 x 53, padding 8 10 8 8, radius 16Tile 36 at radius 8, remove 20 and round, meta gap 1. 8 + 36 + 10 + 235 + 10 + 20 + 10 = 329.

Both 1 pt strokes are drawn inside, so a CSS border would make the shell 363 wide and the attachment row 55. Draw them as inset shadows or inside strokes, which add nothing.

Tokens

Nothing on either component carries a raw color. Each token is resolved in Light and Dark.

Tokens, where, token, light, dark
WhereTokenLightDark
Composer surfacebg/white-0#FFFFFF#0E0E0F
Bubble, discs, tilebg/soft-100#E9E9EA#48484A
Disabled shellbg/weak-50#F4F5F5#242427
Remove controlbg/sub-200#D1D1D6#636366
Both 1 pt strokesborder/soft-100#E9E9EA#2F2F31
Answer, bubble, typed value, file name, papercliptext/strong-950, fg/strong-950#0E0E0F#FFFFFF
Plus glyphfg/sub-700#48484A#C7C7CC
Placeholder, meta, dotstext/soft-500, fg/soft-500#8E8E93#AEAEB2
Dormant and disabled glyphsfg/disabled-400#AEAEB2#8E8E93
Send and Stop discbrand/base#155DFC#2B7FFF
Glyph on that disctext/on-color/strong#FFFFFF#FFFFFF
Error panelstate/error/lighter-50#FEF2F2#FB2C36 at 16%
Error icon and Retry labelstate/error/base-500#FB2C36#FB2C36
Error reasonstate/error/dark-950#460809#FFA2A2
Retry fillcolors/alpha/red/alpha-10#FB2C36 at 10%#FB2C36 at 10%
Composer shadowdropShadow/smeffecteffect

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, and the reason text moves from the darkest red to a light one so it stays readable.

How does the Prompt composer differ from the Input Composer?

The Prompt composer is a multi-line shell with a tools row, a send or stop disc and an optional attachment. The Input Composer is a single 40 high bar with a field, an optional avatar and a send circle.

The Input Composer, on the Input page, is for messages and comments. The field, the avatar and the send circle share one 40 pt height, in a Default or an Overlay style, with Empty and Typing states.

Use the Prompt composer where the person writes a longer instruction to a model, needs tools, attaches a file, or must be able to stop a running answer. Neither contains the other.

How do the AI components differ on iOS and Material 3?

Message and Prompt composer have no iOS or Material 3 counterpart, because neither platform ships a conversation or a prompt component. Build them from this set.

AI components against their iOS and Material 3 counterparts, component, iOS, Material 3, difference
Appetite UIiOS counterpartMaterial 3 counterpartWhat differs
MessageNoneNoneThe platforms leave conversation rows to the app. Assistant text runs the full 361 with no bubble.
Prompt composerThe nearest is iOS Text FieldThe nearest is M3 Text FieldThe native fields draw one field and nothing else. The composer adds the tools row, send and stop, and the attachment row.

The send, stop and tools discs are 32, under the minimum touch target of 44 pt on iOS and 48 dp on Android set by platform/min-touch-target, so extend their hit area in code. See Platform Bridge, iOS and Material 3.

How should a conversation be laid out?

Run the assistant as plain text on the full 361, keep Generating reachable, and announce what arrives.

Do

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.

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

Don't

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.

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

Keep Generating reachable, because Stop is the only control on either component that ends a running answer. The set carries no Streaming and no Error on the User side, so handle a failed send on the composer or as a Toast.

Announce what arrives. Put a streaming answer in a polite live region, move focus to Retry after an error, and give the send and stop disc one accessible name that changes with the state, Send then Stop.