Data Display · astro-ui · Bubble
Bubble
Chat message bubble — 7 variants, start/end align, reactions, groups.
Bubble composes a conversational message from three parts: Bubble sets alignment (start/end) and variant context, BubbleContent is the visible rounded box, and BubbleReactions anchors a reaction pill to an edge. BubbleGroup stacks consecutive same-sender turns with tight spacing. Ghost renders bare content for headings, lists, and code.
Features
- 7 variants: primary, secondary, muted, tinted, outline, ghost, destructive
- start/end alignment for two-sided threads
- BubbleGroup collapses consecutive same-sender turns
- Edge-anchored reactions pill (top/bottom, start/end)
- Interactive content: a button or link as the content gets a themed hover
- Zero JS — wire reactions and actions app-side
Usage
---
import { Bubble, BubbleContent, BubbleReactions } from "astro-ui";
---
<Bubble align="end" variant="primary">
<BubbleContent>Looks great — ship it.</BubbleContent>
<BubbleReactions side="bottom" align="end" ariaLabel="Reactions">
<span aria-hidden="true">🎉</span>
</BubbleReactions>
</Bubble>Demos
Two-sided billing thread
<div class="docs-bubble-thread">
<p class="docs-bubble-meta">Support · 09:12</p>
<Bubble align="start" variant="outline">
<BubbleContent>Hi! I see a double charge on your last invoice.</BubbleContent>
</Bubble>
<p class="docs-bubble-meta docs-bubble-meta--end">You · 09:14</p>
<Bubble align="end" variant="primary">
<BubbleContent>Yes — my card was charged twice for the renewal.</BubbleContent>
</Bubble>
<Bubble align="start" variant="muted">
<BubbleContent>Refunding the duplicate now. It lands in 3–5 business days.</BubbleContent>
</Bubble>
<Bubble align="end" variant="tinted">
<BubbleContent>Confirmed — thank you!</BubbleContent>
</Bubble>
</div>Seven variants in one deploy thread
<div class="docs-bubble-thread">
<Bubble align="end" variant="primary">
<BubbleContent>Deploy the preview build to production.</BubbleContent>
</Bubble>
<Bubble align="start" variant="secondary">
<BubbleContent>On it — building #412.</BubbleContent>
</Bubble>
<Bubble align="start" variant="muted">
<BubbleContent>Last production deploy was v2.4.1, two days ago.</BubbleContent>
</Bubble>
<Bubble align="start" variant="tinted">
<BubbleContent>Build passed in 1m 32s.</BubbleContent>
</Bubble>
<Bubble align="start" variant="outline">
<BubbleContent>3 files changed, 128 additions, 12 deletions.</BubbleContent>
</Bubble>
<Bubble align="start" variant="destructive">
<BubbleContent>Coverage dropped below 80% — consider adding tests.</BubbleContent>
</Bubble>
<Bubble align="start" variant="ghost">
<BubbleContent>Run #412 · runner eu-west-1 · log retained 30 days</BubbleContent>
</Bubble>
</div>Consecutive turns in a BubbleGroup
<div class="docs-bubble-thread">
<p class="docs-bubble-meta">Ada · just now</p>
<BubbleGroup ariaLabel="Three messages from Ada">
<Bubble align="end" variant="primary">
<BubbleContent>Pushed the fix to staging.</BubbleContent>
</Bubble>
<Bubble align="end" variant="primary">
<BubbleContent>Can you re-run the smoke tests?</BubbleContent>
</Bubble>
<Bubble align="end" variant="primary">
<BubbleContent>Then we're clear for the release.</BubbleContent>
</Bubble>
</BubbleGroup>
<p class="docs-bubble-meta">Grace · 1 min ago</p>
<Bubble align="start" variant="muted">
<BubbleContent>Running them now.</BubbleContent>
</Bubble>
</div>Reaction rows
<div class="docs-bubble-thread">
<Bubble align="start" variant="outline">
<BubbleContent>The release notes are ready for review.</BubbleContent>
<BubbleReactions side="bottom" align="end" ariaLabel="Reactions">
<span aria-hidden="true">👍 4</span>
<span aria-hidden="true">🚀 2</span>
</BubbleReactions>
</Bubble>
<Bubble align="end" variant="primary">
<BubbleContent>Great — leaving a celebration on it.</BubbleContent>
<BubbleReactions side="bottom" align="start" ariaLabel="Reactions">
<button type="button" aria-pressed="true" aria-label="Celebrate, 3 reactions">
<span aria-hidden="true">🎉</span> 3
</button>
</BubbleReactions>
</Bubble>
<Bubble align="start" variant="muted">
<BubbleContent>Deployment window opens at 14:00 UTC.</BubbleContent>
<BubbleReactions side="top" align="end" ariaLabel="Reactions">
<span aria-hidden="true">⏰ 1</span>
</BubbleReactions>
</Bubble>
</div>Ghost bubble with rich content
What changed in v2.5
- Bubble components for conversational UIs
- Logical-property separators in
Breadcrumb - 40% faster docs generation
Upgrade with npm install astro-ui@latest.
<div class="docs-bubble-thread">
<Bubble align="start" variant="ghost">
<BubbleContent>
<h4>What changed in v2.5</h4>
<ul>
<li><strong>Bubble</strong> components for conversational UIs</li>
<li>Logical-property separators in <code>Breadcrumb</code></li>
<li><em>40% faster</em> docs generation</li>
</ul>
<p>Upgrade with <code>npm install astro-ui@latest</code>.</p>
</BubbleContent>
</Bubble>
</div>Long answer behind a Collapsible
The pill is absolutely positioned inside the bubble and pushed out with a translate of 75% of its own height, so it straddles the edge without occupying flow space.
A 3px ring in the page background separates the pill from the bubble and any media behind it. side picks the edge (top or bottom) and align picks the horizontal anchor (start or end) — both logical, so they mirror in RTL layouts.
<div class="docs-bubble-thread">
<Bubble align="end" variant="primary">
<BubbleContent>Explain how the reactions pill positions itself.</BubbleContent>
</Bubble>
<Bubble align="start" variant="muted">
<BubbleContent>
<p>
The pill is absolutely positioned inside the bubble and pushed
out with a translate of 75% of its own height, so it straddles
the edge without occupying flow space.
</p>
<Collapsible id="dm-bubble-more">
<span slot="trigger">Show the details</span>
<p>
A 3px ring in the page background separates the pill from the
bubble and any media behind it. side picks the edge (top or
bottom) and align picks the horizontal anchor (start or end) —
both logical, so they mirror in RTL layouts.
</p>
</Collapsible>
</BubbleContent>
</Bubble>
</div>Tinted bubbles as buttons + action row
<div class="docs-bubble-thread">
<Bubble align="start" variant="muted">
<BubbleContent>Two plans fit your usage. Pick one:</BubbleContent>
</Bubble>
<Bubble align="end" variant="tinted">
<button type="button" class="aui-bubble__content">Pro — $19/mo, priority builds</button>
</Bubble>
<Bubble align="end" variant="tinted">
<button type="button" class="aui-bubble__content">Team — $49/mo, shared themes</button>
</Bubble>
<Bubble align="start" variant="outline">
<a class="aui-bubble__content" href="#">Compare all plans</a>
</Bubble>
<div class="docs-bubble-actions">
<Button variant="ghost" size="sm">Regenerate</Button>
<Button variant="outline" size="sm">Copy summary</Button>
</div>
</div>API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
align | '"start" | "end"' | '"start"' | Side of the thread the message sits on. |
variant | '"primary" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive"' | '"primary"' | Color scheme of the content box. |
class | string | — | Merged via cn(); Tailwind classes welcome. |
id | string | — | Element id. |
Slots
| Slot | Description |
|---|---|
default | BubbleContent box and optional BubbleReactions pill. |
Accessibility
Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.
- Reactions row is a labeled group; pressable reactions are real buttons with aria-pressed
- BubbleGroup exposes role=group with an accessible name per sender run
- Variant color pairs keep 4.5:1 contrast on their content
FAQ
How do I make a pressable reaction?
Put a real <button> inside BubbleReactions with aria-pressed; the pill styles it. Toggling state (and the count) is app-side — the component ships zero JS.
How do interactive bubbles work?
Author <button class="aui-bubble__content"> or <a class="aui-bubble__content"> directly instead of BubbleContent — the Bubble root gives interactive content a themed hover and focus ring.
When do I use BubbleGroup?
When one sender sends several turns in a row: group them so spacing tightens and the run reads as a single message block. Give each group an ariaLabel naming the sender.
Changelog
Version history mentioning Bubble.
- Data display (10): Badge, Avatar, Attachment, Table, Progress, Bubble, Chart, Empty, Tag, Timeline.