Bubble
The chat bubble: 7 variants (default, muted, tinted, outline, ghost, destructive, secondary) with optional reactions.
Bubble is the colored rectangle that holds a message in a chat. It is a positioning wrapper (relative + max-width) that also applies the background and padding according to a variant.
Two expected children:
- BubbleContent: the actual text or rich content of the message. In practice a plain
<span>, since the styling (padding, rounded, bg) lives on the parent Bubble. This sub-component exists for API parity with shadcn and so thatBubbleReactionscan target[data-slot=bubble-content]in custom CSS. - BubbleReactions: an absolutely positioned badge (bottom right by default) that holds emojis or reaction buttons. It overflows the bubble via
translate-y-3/4and is framed by aring-white. Optional.
7 variants define the look:
default:bg-primary text-primary-foreground. For messages sent by the current user (strong accent).secondary:bg-secondary text-secondary-foreground. A less saturated alternative.muted:bg-muted text-foreground. For received messages (other participants). The most common in multi-party chat.tinted:bg-primary/10. Accent-tinted, softer than default. Emphasizes without shouting.outline:border bg-background. A bordered rectangle with no fill, an "email draft" look.ghost: no padding or background. For long assistant notes that blend into the text flow without a frame.destructive:bg-destructive/10 text-destructive. For inline errors / warnings.
To stack several bubbles from the same author (runs of short messages, sequential reactions), wrap them in BubbleGroup, a flex flex-col gap-2 that keeps the rendering compact.
Combined with Message for left/right alignment and the avatar, it forms the primitive for any conversation thread.
Installation
$ bext ui add bubble
Usage
Anatomy
<Bubble variant="default|secondary|muted|tinted|outline|ghost|destructive" align="start|end">
├── <BubbleContent>text or rich content</BubbleContent>
└── <BubbleReactions> // optional, absolute badge
└── <span role="img" aria-label="Thumbs up">👍</span> // emoji or <button>
<BubbleGroup> // stacks 2+ Bubbles from the same author
├── <Bubble>...</Bubble>
└── <Bubble>...</Bubble>API Reference
Bubble
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive" | "default" | Sets the background, the text color, and whether there is a border and padding. |
| align | "start" | "end" | "start" | Written to data-align. When passed explicitly it also adds self-start or self-end, which positions the bubble in a flex column. Inside a Message with align="end" the bubble is pushed to the right by the Message, with or without this prop. |
| className | string | — | Additional CSS classes on the root <div data-slot="bubble">. |
BubbleContent
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Semantic wrapper for the content (span). No styling of its own: padding and bg live on Bubble. |
BubbleGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | flex-col gap-2 container for stacking several Bubbles from the same author. |
BubbleReactions
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "bottom" | "bottom" | Vertical position relative to the bubble. "bottom" → below, "top" → above. |
| align | "start" | "end" | "end" | Horizontal alignment. "start" → left-3, "end" → right-3. |
| ariaLabel | string | — | Names the group of reactions: when set, the container gets role="group" and this aria-label. |
| className | string | — | Additional CSS classes. The badge is absolute z-10 with bg-muted + ring-white. |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-slot | every sub-component | "bubble" | "bubble-content" | "bubble-group" | "bubble-reactions" | Stable selector for custom styling. |
| data-variant | Bubble | "default" | "secondary" | ... | Reflects the variant prop. Useful for custom overrides via [data-variant=X] { ... }. |
| data-align | Bubble + BubbleReactions | "start" | "end" | Reflects the align prop. Lets custom CSS target each alignment. |
| data-side | BubbleReactions | "top" | "bottom" | Vertical position. |
Keyboard
| Key | Action |
|---|---|
| Tab | Focuses the first button in BubbleReactions (if present). |
| Enter / Space | Activates the focused button in a reaction. |
Accessibility
A Bubble on its own is a visual container with no ARIA role. The text is directly readable by assistive technologies.
BubbleReactions accepts ariaLabel, which turns the container into a named role="group" (for example "Reactions"). Each reaction still needs its own name on the child you pass in. For a static emoji: <span role="img" aria-label="Thumbs up">👍</span>. For a reaction button: an aria-label on the <button>.
The destructive variant uses text-destructive, which has sufficient contrast in light and dark mode (validated WCAG AA on the shadcn tokens). Check it again if you override the palette.
For a complete accessible thread, wrap the Bubbles in a <Message> with an avatar (to identify the author) and set role="log" aria-live="polite" on the thread container.
Examples
Variants
Alignment (start / end)
BubbleGroup: stacking bubbles from the same author
Reaction placement
Ghost (assistant long-form)
On This Page