For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
Home

Bubble

A chat message bubble with 7 variants, alignment, grouping, and reactions.

Hey, are you free for a call tomorrow?
Sure! What time works for you?
How about 2pm? I can send a calendar invite.
👍 2
2pm works perfectly. See you then!
default variant
secondary variant
muted variant
tinted variant
outline variant
ghost variant
destructive variant
First message in group
Second message in group
Third message in group
import { Bubble, BubbleContent, BubbleGroup, BubbleReactions } from "@/components/ui/bubble";

export const BubbleDemo = () => (
    <div className="flex flex-col gap-6 px-2">
      {/* Conversation thread */}
      <div className="flex flex-col gap-4">
        <Bubble variant="secondary"><BubbleContent>Hey, are you free for a call tomorrow?</BubbleContent></Bubble>
        <Bubble variant="default" align="end"><BubbleContent>Sure! What time works for you?</BubbleContent></Bubble>
        <Bubble variant="secondary">
          <BubbleContent>How about 2pm? I can send a calendar invite.</BubbleContent>
          <BubbleReactions side="bottom" align="start">👍 2</BubbleReactions>
        </Bubble>
        <Bubble variant="default" align="end"><BubbleContent>2pm works perfectly. See you then!</BubbleContent></Bubble>
      </div>
      {/* All variants */}
      <div className="flex flex-col gap-2">
        {(["default", "secondary", "muted", "tinted", "outline", "ghost", "destructive"] as const).map((v) => (
          <Bubble key={v} variant={v}><BubbleContent className="capitalize">{v} variant</BubbleContent></Bubble>
        ))}
      </div>
      {/* BubbleGroup */}
      <BubbleGroup>
        <Bubble variant="secondary"><BubbleContent>First message in group</BubbleContent></Bubble>
        <Bubble variant="secondary"><BubbleContent>Second message in group</BubbleContent></Bubble>
        <Bubble variant="secondary"><BubbleContent>Third message in group</BubbleContent></Bubble>
      </BubbleGroup>
    </div>
  );

Variants

Seven visual styles cover every message type from primary user messages to system errors.

<Bubble variant="default">Current user message</Bubble>
<Bubble variant="secondary">Other participant</Bubble>
<Bubble variant="muted">System note</Bubble>
<Bubble variant="tinted">Soft primary tint</Bubble>
<Bubble variant="outline">Bordered, no fill</Bubble>
<Bubble variant="ghost">Unframed AI response</Bubble>
<Bubble variant="destructive">Error / failed action</Bubble>

Alignment

<Bubble align="start">Incoming — left aligned (default)</Bubble>
<Bubble align="end">Outgoing — right aligned</Bubble>

When placed inside a Message with align="end", the bubble inherits end alignment automatically.

Reactions

BubbleReactions positions an emoji reaction strip relative to the bubble. Use side and align to control placement.

<Bubble variant="secondary">
  <BubbleContent>Great idea!</BubbleContent>
  <BubbleReactions side="bottom" align="end">
    👍 3
  </BubbleReactions>
</Bubble>

Group

Wrap consecutive messages from the same sender in BubbleGroup to reduce vertical spacing between bubbles.

<BubbleGroup>
  <Bubble variant="secondary"><BubbleContent>First message</BubbleContent></Bubble>
  <Bubble variant="secondary"><BubbleContent>Second message</BubbleContent></Bubble>
  <Bubble variant="secondary"><BubbleContent>Third message</BubbleContent></Bubble>
</BubbleGroup>

Props

Bubble

PropTypeDefaultDescription
variant"default" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive""default"Visual treatment
align"start" | "end""start"Inline alignment of the bubble
classNamestring—Additional CSS classes

BubbleContent

PropTypeDefaultDescription
asChildbooleanfalseRender as the child element (e.g. <button>)
classNamestring—Additional CSS classes

BubbleReactions

PropTypeDefaultDescription
side"top" | "bottom""bottom"Vertical anchor relative to the bubble
align"start" | "end""end"Horizontal anchor relative to the bubble
classNamestring—Additional CSS classes

Usage

import {
  Bubble,
  BubbleContent,
  BubbleGroup,
  BubbleReactions,
} from "@/components/ui/bubble";
 
<BubbleGroup>
  <Bubble variant="secondary">
    <BubbleContent>Hello!</BubbleContent>
  </Bubble>
  <Bubble variant="default" align="end">
    <BubbleContent>Hi there!</BubbleContent>
    <BubbleReactions side="bottom" align="end">
      ❤️ 1
    </BubbleReactions>
  </Bubble>
</BubbleGroup>

Specs

# Mint — Bubble Specs
 
## Component
- name: bubble
- file: registry/mint/bubble.tsx
- installs to: components/ui/bubble.tsx
- dependencies: radix-ui, class-variance-authority
- registryDependencies: none
 
## What is it
A chat message bubble for building conversation interfaces. Supports 7 visual
variants to express user messages, AI responses, system notes, and error states.
Prefer Bubble over a plain div when messages need alignment, grouping, or
reaction anchoring; avoid using it for non-conversational content.
 
## Visual identity
Rounded pill-like content areas (rounded-xl) give a friendly chat feel.
Colours now carry Mint's fresh green identity by default — a medium green
fill for outgoing, soft pale-green for incoming — and the tinted variant
derives its hue from --primary for brand coherence without hard-coding a
colour.
 
## Design tokens
--primary: oklch(0.5 0.14 155) / oklch(0.922 0 0)                     — default bubble fill
--primary-foreground: oklch(1 0 0) / oklch(0.1 0 0)                   — default bubble text
--secondary: oklch(0.95 0.04 155) / oklch(0.22 0.04 155)              — secondary bubble fill
--secondary-foreground: oklch(0.32 0.1 155) / oklch(0.88 0.06 155)    — secondary bubble text
--muted: oklch(0.96 0.02 155) / oklch(0.2 0.03 155)                   — muted bubble fill; reactions background
--muted-foreground: oklch(0.52 0.06 155) / oklch(0.6 0.06 155)        — muted bubble text
--background: oklch(1 0 0) / oklch(0.145 0 0)                         — outline bubble fill
--border: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%)     — outline bubble border
--destructive: oklch(0.55 0.25 15) / oklch(0.704 0.191 22.216)        — destructive text; 10–20% alpha fill
--card: oklch(1 0 0) / oklch(0.205 0 0)                               — ring-card on BubbleReactions
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155)                    — focus ring on interactive content
 
## Sub-components
- BubbleGroup     → wraps consecutive same-sender bubbles; flex-col gap-2; reduces visual spacing
- Bubble          → root container; holds variant + align; w-fit max-w-[80%]; positions reactions
- BubbleContent   → styled content area; rounded-xl border px-3 py-2 text-sm; accepts asChild
- BubbleReactions → absolute emoji strip anchored to bubble corner; bg-muted ring-card
 
## Variants
- default     → --primary fill, --primary-foreground text. Current user's outgoing messages.
- secondary   → --secondary fill. Incoming messages from other participants.
- muted       → --muted fill, no explicit text override. Low-emphasis system messages or timestamps.
- tinted      → oklch-derived primary tint (light: ~0.93 L; dark: ~0.30 L). AI suggestions or highlighted replies.
- outline     → --border border, --background fill, no colour override. Lightweight confirmations or quoted messages.
- ghost       → no border, no fill, no padding, no radius. Long-form AI prose; max-w-full.
- destructive → --destructive/10 fill (dark: /20), --destructive text. Error states, failed actions, critical warnings.
 
## States
- hover (button/a child):  default darkens to primary/80; secondary/muted/outline/ghost show a muted tint via color-mix or bg-muted
- focus-visible (button/a): 3px ring in --ring/50, border switches to --ring
- data-[align=end]:         bubble self-aligns to flex-end (right); also responds to parent group-data-[align=end]/message
- data-[variant=ghost]:     overrides max-w-[80%] to max-w-full
 
## Conventions
- Use align="end" for the current user's messages; align="start" (default) for all others
- Wrap consecutive messages from the same sender in BubbleGroup to reduce vertical spacing
- BubbleReactions is absolutely positioned — the parent Bubble must not have overflow-hidden
- Colour styles target *:data-[slot=bubble-content] via CSS child selectors; sibling elements (reactions, footers) remain unstyled by the variant
- Do not mix multiple variants inside one BubbleGroup
- Sub-components expose data-slot="bubble-group", data-slot="bubble", data-slot="bubble-content", and data-slot="bubble-reactions" for CSS targeting

Installation

GIN Design Plugin
$ npx shadcn add https://gin-design.vercel.app/bubble