120k

气泡

以消息气泡的形式显示对话内容。支持变体、对齐、分组、表情反应和可折叠内容。

Hey there! what's up?
Hey! Want to see chat bubbles?
I can group messages, switch sides, and keep the whole thread easy to scan.
Sure. Hit me with your best demo.
Yes. You are reading a demo that is demoing itself. Very meta. Very on-brand.
import {
  Bubble,
  BubbleContent,

Bubble 组件用于显示带边框的对话内容。可用于聊天文本、简短的结构化输出、引用回复、建议和表情反应。

对于功能更完整的聊天界面,请使用 Message 组件。Bubble 有意仅聚焦于气泡本身。请将头像、名称、时间戳、元数据以及消息级操作放在 Message 中。

安装

pnpm dlx shadcn@latest add bubble

用法

import { Bubble, BubbleContent, BubbleReactions } from "@/components/ui/bubble"
<Bubble>
  <BubbleContent>
    我检查了注册表输出并移除了过期路由。
  </BubbleContent>
  <BubbleReactions>
    <span>👍</span>
  </BubbleReactions>
</Bubble>

组合

使用以下组合来构建气泡:

气泡
├── 气泡内容
└── 气泡反应

使用 BubbleGroup 将来自同一发送者的连续气泡分组:

气泡组
├── 气泡
│   └── 气泡内容
└── 气泡
    └── 气泡内容

特性

  • 七种视觉变体,从强烈的主色气泡到无边框的幽灵内容
  • 发件人和接收者气泡的起始和结束对齐
  • 锚定到气泡边缘的反应,支持可配置的侧边和对齐方式
  • 气泡会根据内容自适应大小,最大可达容器宽度的 80%
  • 通过 render 为链接和按钮气泡提供多态内容
  • 通过每个部分上的 className 属性实现可自定义样式

示例

变体

使用 variant 来改变气泡的视觉样式。

This is the default primary bubble.
This is the secondary variant.
This one is muted. It uses a lower emphasis color for the chat bubble.
This one is tinted. The tint is a softer color derived from the primary color.
We can also use an outlined variant.
Or a destructive variant with a reaction.

Ghost bubbles work for assistant text, markdown, and other content that should not be framed.

This is perfect for assistant messages that should not have a frame and can take the full width of the container. You can also render code in it.

Ghost bubbles are full width and can take the full width of the container.

import { Markdown } from "@/components/markdown"
import {
  Bubble,
变体描述
default强调的主气泡,通常用于当前用户。
secondary用于对话内容的标准中性气泡。
muted用于安静辅助内容的低强调气泡。
tinted带有轻微主色调的气泡。
outline用于次要或富内容的带边框气泡。
ghost用于助手文本或富内容的无边框内容。
destructive用于错误或失败操作的破坏性气泡。

气泡的大小会随其内容而变化,最大宽度为容器宽度的 80%。ghost 变体会移除最大宽度限制,因此助手文本和富内容可以占据整行。

对齐

Bubble 上使用 align 来将气泡对齐到对话的开头或结尾。

This bubble is aligned to the start. This is the default alignment.
This bubble is aligned to the end. Use this for user messages.
import { Bubble, BubbleContent } from "@/components/ui/bubble"

export function BubbleAlignmentDemo() {
align描述
start将气泡对齐到对话的开头。
end将气泡对齐到对话的结尾。

注意: 在构建聊天界面时,你可能希望在 Message 组件本身上使用对齐,而不是 Bubble 组件。你可以在 Message 组件上使用 role 属性,自动将气泡对齐到对话的开头或结尾。

气泡组

使用 BubbleGroup 将来自同一发送者的连续气泡分组。请注意,align 属性应设置在 Bubble 组件本身上,而不是 BubbleGroup 组件上。

BubbleGroup
├── Bubble
│   └── BubbleContent
└── Bubble
    └── BubbleContent
Can you tell me what's the issue?
You tell me!
It worked yesterday. You broke it!
Find the bug and fix it.
👀
Want me to diff yesterday's you against today's you? It's a bit embarrassing.
import {
  Bubble,
  BubbleContent,

链接和按钮

你可以通过在 BubbleContent 上使用 render 属性,将气泡变成链接或按钮。

How can I help you today?
"use client"

import { toast } from "sonner"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
 
export function BubbleLinkDemo() {
  return (
    <Bubble variant="muted">
      <BubbleContent render={<button />}>点击这里</BubbleContent>
    </Bubble>
  )
}

反应

使用 BubbleReactions 来显示气泡反应。你可以用它来展示反应或快捷操作按钮。使用 sidealign 来定位这一行——side="top" 会将其固定在上边缘。反应会与气泡边缘重叠,因此行与行之间要留出垂直空间——下面的示例因此使用了更大的 gap

I don't need tests, I know my code works.
Bold. Fine I'll add some tests. I'll let you know when they're done.
Tests passed on the first try. All 142 of them. Looking good!
Are you sure I can run this command?
"use client"

import { toast } from "sonner"

展开更多 / 可折叠

较长的气泡内容可以与 Collapsible 组合使用,以实现展开更多或收起的交互。使用 CollapsibleTrigger 组件来触发可折叠内容。

How can I help you today?
The accessibility review found two focus states that were visually too subtle in dark mode. I checked the dialog, menu, and drawer paths because each one renders focusable control...
"use client"

import * as React from "react"

提示框

将气泡包裹在 Tooltip 中,以在悬停时显示元数据,例如消息何时被读取。

Did you remove the stale route?
Yes, removed it from the registry.
import { CheckIcon } from "lucide-react"

import {

弹出框

将气泡与 Popover 配对,以按需显示更多信息,例如失败操作的完整错误消息。

Run the build script.
Failed to run the command.
import { InfoIcon } from "lucide-react"

import {

可访问性

Bubble 渲染的是展示性消息表面。请将会话级语义保留在周围容器上,并遵循以下指南。

为反应添加标签

反应会以一行 emoji 的形式渲染。屏幕阅读器会在没有上下文的情况下逐个读取每个符号,而像 +8 这样的计数会被读作“加八”。请将这一行作为单个图像分组,并添加描述性的 aria-label,这样它只会被播报一次。role="img" 还会将单个 emoji 从辅助技术中隐藏,因此不需要 aria-hidden

<BubbleReactions role="img" aria-label="反应:点赞、火焰,还有 8 个更多">
  <span>👍</span>
  <span>🔥</span>
  <span>+8</span>
</BubbleReactions>

当反应是可交互的时,请改为渲染按钮,并为仅图标按钮添加 aria-label

<BubbleReactions>
  <Button aria-label="点赞" variant="secondary" size="icon-xs">
    <ThumbsUpIcon />
  </Button>
</BubbleReactions>

可交互气泡

当气泡可点击时,请使用 render 属性将其渲染为真正的 <button><a>,这样它就可以获得焦点并暴露正确的角色。BubbleContent 会为可交互元素提供可见的焦点环,且可访问名称来自气泡文本。不需要额外的标签。

<Bubble variant="muted" align="end">
  <BubbleContent render={<button type="button" onClick={onReply} />}>
    我忘记了我的密码
  </BubbleContent>
</Bubble>

超越颜色的含义

Bubble 变体通过颜色传达角色和语气。请将它们与文本、对齐方式或图标搭配使用,以免含义仅靠颜色来传达。对于 destructive 气泡,请将错误上下文保留在消息文本中,而不是依赖颜色样式。

API 参考

Bubble

根气泡包装器。

PropTypeDefaultDescription
variant"default" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive""default"气泡的视觉样式。
align"start" | "end""start"气泡的行内对齐方式。
classNamestring-应用于根元素的附加类。

BubbleContent

气泡内容包装器。

PropTypeDefaultDescription
renderReactElement | function-将内容渲染为不同的元素,例如链接。
classNamestring-应用于内容元素的附加类。

BubbleReactions

显示气泡上重叠的反应。

PropTypeDefaultDescription
side"top" | "bottom""bottom"反应锚定到气泡的哪一侧。
align"start" | "end""end"反应的行内对齐方式。
classNamestring-应用于反应行的附加类。

BubbleGroup

将来自同一发送者的连续气泡分组。

PropTypeDefaultDescription
classNamestring-应用于组根节点的附加类。