Skip to content

Chat — Chat · Message · Bubble · Suggestions · Projection · Attachment · Marker · Composer · Message scroller

Composer — where the user writes to Echo, and it is ONE LINE.

Beta · Chat · packages/css/src/components/chat.css · React on Base UI: @pepperui/chat · @pepperui/attachment · @pepperui/bubble · @pepperui/marker · @pepperui/message · @pepperui/message-scroller · @pepperui/projection · @pepperui/suggestion-row · @pepperui/suggestions

.pui-chat (__strip __logo __live __dot) · .pui-message-scroller (__log __jump) · .pui-message (--user --ai; __avatar __body __actions) · .pui-bubble (--user --ai --thinking; __meta __meta--end __actions __link) · .pui-suggestions (__head __row __row--tentative __name __bar __track __fill __value __confidence __pill __budget) · .pui-projection-row / .pui-projection (__label __value __value--good) · .pui-marker (--divider) · .pui-composer (__files __bar) · .pui-attachment (__glyph __name __remove).

Chat — Chat · Message · Bubble · Suggestions · Projection · Attachment · Marker · Composer · Message scroller — every size and every stateOpen in the workshop

Sizes across, states down.

  • Has files

Use when a chat accepts a message.

Do not use when a form field is wanted (Input with Field).

Relates to Input, Button, Attachment, Chat.

Anti-patterns a round icon button in place of the labelled one · a floating card with a shadow · a 64-tall textarea.

Keywords: composer, input, ask, send, chat, prompt

the Echo AI chat screen: Composer + Message scroller + Messages; the block’s own set is the Composer

Figma — page Chat: Composer (1 variants) · Chat (1 variants)

Attachment — a file or an image as a chip, in the composer before sending and in a message after.

  • Name; Type: file · image; Has remove (on in the composer, off in a message)

Use when a file or an image travels with a message — in the Composer’s files row, and echoed in the sent Message.

Do not use when a file in a list (Item with a file glyph); an image in the content (Aspect ratio); a tag (Chip).

Relates to Composer, Message, Chip, Icon.

Anti-patterns a chip without its glyph · a remove on a sent attachment · a thumbnail larger than the chip · a fill changed on the instance.

Keywords: attachment, file chip, image chip, upload, composer

Figma — page Chat: Attachment (4 variants)

Bubble — the surface a message sits on, and the two roles are no longer a light one and a dark one.

  • Text · Meta · Time · Link · Link 2 · Has link 2 · Has actions · Content · Has content · Has meta; Role: user · ai; State: default · thinking

Use when a turn of a conversation needs a surface.

Do not use when the text is a system line (Marker); a label is wanted (Badge).

Relates to Message, Suggestions, Projection, Chat.

Anti-patterns a dark user bubble · a tail corner · text/subtle for a 12 meta, which the matrix promises only at 24 and over · an actions row added with its own margin instead of the block’s gap.

Keywords: bubble, message, chat, speech, turn, ai, echo

Figma — page Chat: Bubble (4 variants)

Marker — a system line in the thread: a date divider (‘Today’) or a note (Echo can read its 18 slides’, ‘Context switched to MAFO’).

  • Text; Type: divider · note; 760 wide, the pattern’s column

Use when the thread changes state — a new day, an attachment read, a context change, a handover to a person.

Do not use when the AI has something to say (Message); an error (Toast - status or Alert in the thread’s Message).

Relates to Message scroller, Message, Separator, Attachment.

Anti-patterns a marker for every message · a marker in a bubble · two markers in a row · a fill changed on the instance.

Keywords: marker, divider, system note, thread, date

Figma — page Chat: Marker (2 variants)

Message — one turn: an avatar, and what was said.

  • Has actions; Role: user · ai

Use when a conversation is rendered turn by turn.

Do not use when a single bubble is shown on its own.

Relates to Bubble, Message scroller, Avatar, Chat.

Anti-patterns a turn without its avatar · a name or a second mark beside an avatar that already carries the brand’s own · a feedback row on the user’s own words.

Keywords: message, turn, chat, avatar, feedback

Figma — page Chat: Message (2 variants)

Message scroller — the thread: it owns the scroll.

  • Has jump to latest · Has marker

Use when turns need to scroll under a fixed composer.

Do not use when the whole conversation is short and static.

Relates to Message, Marker, Chat.

Anti-patterns a log that fills its box while a pill sits under it, which cuts the newest message · a scroller with no bottom anchor.

Keywords: scroller, thread, log, conversation, scroll, jump

Figma — page Chat: Message scroller (1 variants)

Projection — the numbers Echo commits to, under the answer that promised them.

  • Label 1–4 and Value 1–4, Has chip 3 and Has chip 4 for fewer, and Time, which closes the row

Use when an answer promises numbers worth restating.

Do not use when the values need comparing against each other (Suggestions).

Relates to Bubble, Suggestions.

Anti-patterns using the library’s Chip, which is a tint label and a different component · a green on a number that is not good news.

Keywords: projection, chips, forecast, roas, cpa, commitment

3.9.2 — the numbers Echo commits to, as chips under the answer that promised them.

Figma — page Chat: Projection (1 variants)

Suggestion row — one line of Echo’s proposal: a name, a measure with its number, a confidence pill and a budget.

  • Name · Value · Confidence · Budget; Tone: confident · tentative

Use when a row of Suggestions is needed.

Do not use when a generic table row is wanted (Table row).

Relates to Suggestions, Bubble.

Anti-patterns a per-row bar length set on the instance, which does not hold · a green figure as the only cue that a row is good news.

Keywords: suggestion, row, rank, bar, confidence, pill, budget

3.9.2 — one line of the proposal: name, measure, confidence, budget; Tone confident / tentative.

Figma — page Chat: Suggestion row (2 variants)

Suggestions — Echo’s ranked proposal, inside its bubble: a surface/card block at radius/8 padded space/12 at a space/8 gap, a head of 12 SemiBold labels in text/caption over rows on surface/page at radius/4.

  • Five EXPOSED Suggestion rows — a consumer sets each row’s Name, Value, Confidence, Budget and Tone on the instance — with Has row 4 and Has row 5 for a shorter ranking

Use when the assistant proposes a ranked set a person can apply.

Do not use when the numbers are a report to read (Data table).

Relates to Suggestion row, Bubble, Table.

Anti-patterns a head label painted in its column’s own colour · placing it outside the bubble.

Keywords: suggestions, ranking, publishers, roas, confidence, budget, table

3.9.2 — Echo’s ranked proposal inside its bubble; the block, its head and its rows.

Figma — page Chat: Suggestions (1 variants)

The class, the parts, and nothing that is not in the package. The same block opens chat.css.

<div class="pui-chat">
<div class="pui-chat__strip"><img class="pui-chat__logo" src="…/echo-ai-wordmark.svg" alt="Echo AI" />
<span class="pui-chat__live"><span class="pui-chat__dot"></span>Live · model v4.2</span></div>
<div class="pui-message-scroller"><div class="pui-message-scroller__log">
<div class="pui-marker pui-marker--divider">Today</div>
<div class="pui-message pui-message--user">
<div class="pui-bubble pui-bubble--user"><p>…</p><p class="pui-bubble__meta">Anna · 10:42</p></div>
<span class="pui-avatar pui-message__avatar"><img src="…/anna.png" alt="" /></span></div>
<div class="pui-message pui-message--ai">
<img class="pui-message__avatar" src="…/echo-mark.svg" alt="" />
<div class="pui-bubble pui-bubble--ai"><p>…</p>
<div class="pui-suggestions">…</div>
<div class="pui-bubble__actions"><button class="pui-button">Apply suggestions</button>
<a class="pui-bubble__link" href="#">Explain the reasoning</a>
<span class="pui-bubble__meta pui-bubble__meta--end">Echo · 10:42 · 1.8 s</span></div></div></div>
<div class="pui-message pui-message--ai"><img class="pui-message__avatar" src="…" alt="" />
<div class="pui-bubble pui-bubble--ai pui-bubble--thinking">Thinking…</div></div></div>
<button class="pui-button pui-button--secondary pui-button--small pui-message-scroller__jump">Jump to latest</button></div>
<form class="pui-composer">
<div class="pui-composer__files"><span class="pui-attachment">…</span></div>
<div class="pui-composer__bar"><input class="pui-input" aria-label="Ask Echo" placeholder="Ask Echo about publishers, GEOs, creatives or budget…" />
<button class="pui-button" type="button">Ask Echo</button></div></form></div>

Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.

No story of this component renders a text pair axe can measure — a component made of surfaces and borders rather than words. The token pairs it uses are in artifacts/contrast-matrix.md.

primitive on the indicator over the change
Attachment remove surface/page shadow #6a66e4 2 px #ffffff 4.53 ok
Attachment remove surface/card shadow #6a66e4 2 px #f2f2f7 4.06 ok
Attachment remove surface/sunken shadow #6a66e4 2 px #f2f2f2 4.05 ok
Chat action link surface/page shadow #6a66e4 2 px #ffffff 4.53 ok

10 of 10 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.

Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.

Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).

Pepper UI v1.5.0

The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.