Chat
The Supporter Response building blocks: contact avatars and rows, tags, message bubbles, journey links, timeline events, the reply composer and contact details. Colors come from Colors/chat.
Figma: 10 / Components / ChatBrandon shared their mission story inYou are not alone
I lost my grandmother to COPD and want to ensure future generations don’t suffer.
Brandon is waiting for a response
Usage
Source: src/components/ui/chat.tsx
import { MessageBubble, ThreadMessage, TimelineEvent } from '@/components/ui/chat'<ThreadMessage context='Brandon shared their mission story in' journey='You are not alone'>
<MessageBubble>I lost my grandmother to COPD and want to ensure future generations don’t suffer.</MessageBubble>
</ThreadMessage>
<TimelineEvent kind='waiting'>Brandon is waiting for a response</TimelineEvent>ContactAvatar
A 40px avatar on Colors/surface/muted with the mission (heart) and high-intent (rocket) indicators above it. Pass src for an approved photo.
BSGLJM
Tags
Default and AI suggested tags, each with an optional remove. Suggested tags keep the dashed Colors/chat/ai/accent border; selecting one adds it.
Host EventMonthly Donor
Message bubbles
One bubble per direction, Roboto 16 / 24 with 16px corners. Incoming has a border; outgoing squares its top left corner.
I lost my grandmother to COPD and want to ensure future generations don’t suffer.
I lost my grandmother to COPD and want to ensure future generations don’t suffer.
Thread details
JourneyLink belongs with message context; ContactGroupHeader groups the contact list by day.
You are not alone
Yesterday5
Contact rows
Two-line rows in default, unread (medium summary and a dot), selected (Colors/chat/row/selected) and done (Colors/surface/subtle). Select a row to try it.
Timeline events
Status lines in the conversation. The AI draft uses the GoodUnited AI sparkle.
Brandon is waiting for a response
GoodUnited AI drafted a response for Brandon
You replied · 12:36 AM, Aug 16, 2026
You copied the response and replied via email
Marked as done
Reply channels
Messenger or email for the reply. Email switches the composer to copy-and-open.
Reply composer
The drafted reply in the composer gradient frame, the writing-style and feedback footer, and the already-sent guard. Send, then Write another.
Reply composer, email
channel email adds the subject and ends with Copy and open email.
Contact details
One line each for the location, the Messenger name and the email in the supporter profile.
Location: Portland, Oregon
Messenger: Brandon Smith
Email: brandon@example.com
When to use
When to reach for it, and when to use something else.
Use it for
Complete: Supporter Response screens, composed into ContactList, ConversationPane and SupporterProfile (Blocks / Supporter Response).
Complete: Incoming bubbles for what supporters shared, outgoing bubbles for replies that went out.
Complete: The suggested tag kind only for tags GoodUnited AI proposed.
Avoid
Not complete: The Colors/chat tokens outside Supporter Response; use the status and surface tokens elsewhere.
Not complete: A second composer on the same conversation; show SentNotice once a reply went out.
ContactRow
| Prop | Type | Default | Description |
|---|---|---|---|
| initials | string | – | Shown in the ContactAvatar. |
| summary | ReactNode | – | Name and topic, Roboto / text-sm. |
| preview | ReactNode | – | The latest message, one line, truncated. |
| time | string | – | Roboto / text-xs at the right. Leave it out on done rows. |
| state | 'default' | 'unread' | 'selected' | 'done' | 'default' | Background and text weight. |
| mission, intent | boolean | true | The avatar indicators. |
ChatTag
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | – | The tag name. |
| kind | 'default' | 'suggested' | 'default' | Suggested adds the dashed AI border. |
| onSelect | () => void | – | Makes the tag a button, used to accept a suggestion. |
| onRemove | () => void | – | Shows the remove x. |
TimelineEvent
| Prop | Type | Default | Description |
|---|---|---|---|
| kind | 'waiting' | 'ai-draft' | 'replied' | 'email-replied' | 'done' | – | The icon and its color. |
ReplyComposer
| Prop | Type | Default | Description |
|---|---|---|---|
| channel | 'messenger' | 'email' | – | Messenger ends with Send; email adds the subject and Copy and open email. |
| message, onMessageChange | string, (value: string) => void | – | The drafted reply. Send is disabled while it is empty. |
| subject, onSubjectChange | string, (value: string) => void | – | Email only. |
| onSend, onCopyEmail | () => void | – | The primary action for each channel. |
ComposerFooter
| Prop | Type | Default | Description |
|---|---|---|---|
| onWritingStyle | () => void | – | Opens the writing-style settings. |
| feedback, onFeedback | 'up' | 'down' | null | – | Thumbs up and down on the draft; pressing again clears it. |