Bubble
A hand-drawn chat bubble in a received or sent variant.
Overview
Bubble is a single chat bubble — a sketchy rounded box holding message text. It comes in two
variants: received (a muted incoming bubble) and sent (a filled outgoing bubble in the primary
colour). It only paints itself; alignment within a conversation is the caller’s concern (see the
Message component).
Anatomy
A hand-drawn rounded box (outline + fill from @ghds/sketch-core) wrapping inline message text.
Colour, padding, radius, and sketch parameters come from @ghds/tokens (comp.bubble.*). The box
is inline-block with max-width: 100%, so long text wraps rather than overflowing.
Variants & States
Two variants, no interactive states — the bubble is static content.
| State | Status | Notes |
|---|---|---|
| received | Implemented | A muted incoming bubble. |
| sent | Implemented | A filled outgoing bubble in the primary colour. |
| Hover / Focus | Not implemented | Not interactive on its own. |
| Disabled | Not implemented | — |
| Loading | Not implemented | — |
Usage
Do
- Use received for incoming messages and sent for the current user’s outgoing ones.
- Wrap bubbles in Message to handle avatar, author, timestamp, and alignment.
- Constrain the bubble’s max width in long threads so lines stay readable.
Don't
- Don't rely on Bubble for alignment — it paints itself but doesn't position within a row.
- Don't put interactive controls that need their own semantics directly inside a bubble without proper roles.
Accessibility
- The bubble is a plain container; the meaning is its text content. Colour distinguishes sent from
received, but the conversation structure (who said what) should come from
Message’s author and layout, not colour alone. - The sketch box is decorative and never changes what is announced.
- See the Accessibility Guide for shared conventions.
Content
Keep bubble content to the message itself. Author and timestamp belong in the surrounding Message
header, not inside the bubble.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'received' | 'sent' | 'received' | 'received' is a muted incoming bubble; 'sent' is a filled outgoing bubble. |
...rest | HTMLAttributes<HTMLDivElement> | — | Native <div> attributes (style for max-width, etc.) pass through. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'received' | 'sent' | 'received' | Reflected attribute; 'received' is muted incoming, 'sent' is a filled outgoing bubble. |
slot | (default) | — | The message content. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'received' | 'sent' | 'received' | 'received' is a muted incoming bubble; 'sent' is a filled outgoing bubble. |
children | ReactNode | — | Bubble content. |
testID | string | — | Test handle for queries. |
Code examples
// React — @ghds/react
import { Bubble } from '@ghds/react/bubble';
<Bubble variant="received">Hey — are we still on for tomorrow?</Bubble>
<Bubble variant="sent">Yes! See you at 10.</Bubble>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
import '@ghds/web-components/bubble';
</script>
<gh-bubble variant="received">Hey — are we still on for tomorrow?</gh-bubble>
<gh-bubble variant="sent">Yes! See you at 10.</gh-bubble>
// React Native — @ghds/react-native
import { Bubble } from '@ghds/react-native/bubble';
<Bubble variant="received">Hey — are we still on for tomorrow?</Bubble>
<Bubble variant="sent">Yes! See you at 10.</Bubble>;
Live Demo
React and Web Components render live below, driven by the same tokens — toggle dark mode in the
header to see both respond identically. React Native can’t run in a browser without additional
react-native-web wiring (not set up in this site), so its usage is shown as a code sample instead.