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).
Sizes across, states down.
Chat pattern — Beta
Section titled “Chat pattern — Beta”- 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 — Beta
Section titled “Attachment — Beta”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 — Beta
Section titled “Bubble — Beta”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 — Beta
Section titled “Marker — Beta”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 — Beta
Section titled “Message — Beta”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 — Beta
Section titled “Message scroller — Beta”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 — Beta
Section titled “Projection — Beta”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 — Beta
Section titled “Suggestion row — Beta”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 — Beta
Section titled “Suggestions — Beta”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 markup — copy this
Section titled “The markup — copy this”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>Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”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.
Contrast
Section titled “Contrast”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.
The focus ring
Section titled “The focus ring”| 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.
Keyboard
Section titled “Keyboard”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.