# SUI > SUI 是基于 Base UI 和 Tailwind CSS 构建的可组合 React 组件库。本文档包含工作区中的完整示例源码 # CLI 使用 shadcn 搜索、查看、安装和更新 SUI 源码 页面: https://sui.draco.dev/zh-CN/docs/cli SUI 使用 shadcn CLI。在消费应用中完成 [registry 配置](/zh-CN/docs/installation#prepare-the-consuming-application),然后在该应用目录运行命令。 ## 搜索与查看 ```bash bunx --bun shadcn@latest search @sui -q input bunx --bun shadcn@latest view @sui/sensitive-input ``` 搜索匹配条目名称与描述。查看命令可在安装前检查文件和依赖。[Registry 指南](/zh-CN/docs/registry)介绍索引和条目的格式。 ## 安装源码 ```bash bunx --bun shadcn@latest add @sui/button @sui/sensitive-input bunx --bun shadcn@latest add @sui/data-table ``` CLI 将所需源码复制到配置的 `ui`、`components`、`lib` 和 `hooks` 目录,安装包依赖,并将 SUI 样式合并到 `components.json` 指定的 CSS 文件。安装后的文件属于应用,可以直接修改。 每个条目包含完整的本地依赖,安装单个组件时无需先安装整个组件库。`@sui/sui` 安装完整集合,`@sui/sui-style` 安装共享 CSS,`@sui/sui-theme` 安装主题工具与样式。 Editor 和完整集合需要本地 Monaco worker 支持,详见[兼容性说明](/zh-CN/docs/compatibility#editor-workers)。 ## 检查更新 ```bash bunx --bun shadcn@latest add @sui/button --dry-run bunx --bun shadcn@latest add @sui/button --diff ``` 更新可能同时影响共享依赖和 CSS。覆盖文件前先对比本地修改。安装属于源码复制,文件不会自动跟随 SUI 仓库的后续变更。 ## 常见问题 | 现象 | 检查项 | | --- | --- | | 找不到 `@sui` registry | 在消费应用目录执行,检查该应用的 `components.json` | | GitHub 返回 `404` | 检查配置的地址,以及 registry 是否包含所需条目 | | 导入路径与文档示例不同 | 使用配置的别名替换工作区路径,见[导入映射](/zh-CN/docs/installation#import-installed-source) | | 缺少动画或主题样式 | 确认应用已加载配置的 CSS 文件 | | Monaco worker 导入失败 | 使用兼容的 `?worker` 加载器,并包含安装的类型声明 | 通过助手安装时参阅 [MCP](/zh-CN/docs/mcp),读取 API 和示例文本时参阅 [LLMs](/zh-CN/docs/llms-txt)。 --- # LLMs 以纯文本读取 SUI 文档和完整示例 页面: https://sui.draco.dev/zh-CN/docs/llms-txt 通过 LLM 接口向助手提供当前 SUI API 和示例。这些接口用于读取文档;安装组件源码时使用 [MCP](/zh-CN/docs/mcp) 或 [CLI](/zh-CN/docs/cli)。 ## 文档入口 | 内容 | English | 简体中文 | | --- | --- | --- | | 页面索引 | [`/llms.txt`](/llms.txt) | [`/zh-CN/llms.txt`](/zh-CN/llms.txt) | | 完整文档 | [`/llms-full.txt`](/llms-full.txt) | [`/zh-CN/llms-full.txt`](/zh-CN/llms-full.txt) | | 单个组件 | [Button Markdown](/api/llms-markdown?locale=en-US&slug=components/button) | [Button Markdown](/api/llms-markdown?locale=zh-CN&slug=components/button) | 这些路径属于当前文档站的访问地址。GitHub raw 托管 [registry](/zh-CN/docs/registry) JSON,LLM 接口则由文档站提供。 索引按指南、组件、Blocks 和工具整理页面,并链接到对应 Markdown。建议先读取索引,再读取任务相关页面。完整文档包含所有页面,通常比单次请求所需的上下文更大。 ## 读取单个页面 `/api/llms-markdown` 接受 `locale` 和 `slug`: | 参数 | 可选值 | 默认值 | | --- | --- | --- | | `locale` | `en-US`、`zh-CN` | `en-US` | | `slug` | 不含 `/docs/` 的文档路径,如 `components/button`、`blocks/data-table` 或 `theming` | 省略时返回简介 | 无效语言和不存在的文档返回 `404`。显式访问 `/en-US/llms.txt` 或 `/en-US/llms-full.txt` 时,会重定向到没有语言前缀的英文地址。 [读取 Sensitive Input Markdown](/api/llms-markdown?locale=zh-CN&slug=components/sensitive-input) 结果包含标题、描述、页面地址、API 说明,以及该页面共享示例的完整源码。预览和代码展示读取同一份示例文件,纯文本内容也与可运行文档保持一致。 ## 从页面复制 页面头部提供**复制 Markdown**和**打开**。复制 Markdown 获取原始 MDX 文档;打开菜单可将展开后的 LLM Markdown 提供给助手。程序读取时,`/api/markdown` 返回原始文档,`/api/llms-markdown` 会将共享示例展开为代码块。 ## 请求示例 ```text 读取 SUI 的 llms.txt,再阅读 Field 和 Sensitive Input 文档。 组合一个带标签的敏感输入框,校验信息由应用提供。 验证逻辑和翻译文案保留在应用中。 安装源码后,使用我的 components.json 中配置的导入别名。 ``` 文档示例使用 `@workspace/ui/components/button` 等工作区路径。安装源码后使用应用自身的别名,映射方式见[安装指南](/zh-CN/docs/installation#import-installed-source)。读取文档不会安装文件或配置 MCP 客户端。 --- # MCP 将 AI 助手连接到 SUI registry 页面: https://sui.draco.dev/zh-CN/docs/mcp 使用官方 [shadcn MCP 服务](https://ui.shadcn.com/docs/mcp),让 AI 助手浏览 SUI registry。服务通过 stdio 在本地运行,并读取消费应用的 `components.json`。SUI 提供 registry JSON 文件;registry URL 是 HTTP 数据源,不是 MCP 服务地址。 需要 API 说明和完整使用示例时,向助手提供 [LLMs 文档](/zh-CN/docs/llms-txt)。MCP 用于查询和查看可安装源码。其 `get_add_command_for_items` 工具返回 CLI 命令,随后由助手在应用中执行命令安装文件。 ## 配置应用 按照[安装指南](/zh-CN/docs/installation)准备消费应用。将以下 registry 条目合并到应用现有的 `components.json`,保留应用的别名和 CSS 配置: ```json { "registries": { "@sui": "https://raw.githubusercontent.com/draco-china/sui/main/registry/r/{name}.json" } } ``` 目录索引为 `https://raw.githubusercontent.com/draco-china/sui/main/registry/r/registry.json`。CLI 与 MCP 使用同一份配置。 在 MCP 客户端中打开消费应用,以包含其 `components.json` 的目录作为服务工作目录。在 monorepo 中,使用应用目录,而非没有该文件的仓库根目录。[CLI](/zh-CN/docs/cli) 也使用同一份配置。 ## 配置客户端 以下示例使用 Bun 启动官方服务: ```bash bunx --bun shadcn@latest mcp ``` MCP 客户端启动此进程,通过 stdin/stdout 通信。将对应配置合并到项目现有的客户端配置文件,然后重启或启用服务。客户端进程的环境中必须能找到 Bun。 ### Claude Code 使用项目的 `.mcp.json`: ```json { "mcpServers": { "shadcn": { "command": "bunx", "args": ["--bun", "shadcn@latest", "mcp"] } } } ``` 重启 Claude Code,使用 `/mcp` 检查连接状态。 ### Cursor 使用项目的 `.cursor/mcp.json`: ```json { "mcpServers": { "shadcn": { "command": "bunx", "args": ["--bun", "shadcn@latest", "mcp"] } } } ``` 在 Cursor 的 MCP 设置中启用 shadcn 服务,并确认工具已列出。 ### VS Code 使用 GitHub Copilot 时,配置项目的 `.vscode/mcp.json`。VS Code 使用 `servers` 字段: ```json { "servers": { "shadcn": { "command": "bunx", "args": ["--bun", "shadcn@latest", "mcp"] } } } ``` 打开文件,通过 VS Code 的 MCP 控件启动服务。客户端配置文件位置与字段遵循 [shadcn 官方配置指南](https://ui.shadcn.com/docs/mcp#configuration)。 ### Codex 将以下配置合并到消费应用的 `.codex/config.toml`。将占位路径替换为应用的绝对目录: ```toml [mcp_servers.shadcn] command = "bunx" args = ["--bun", "shadcn@latest", "mcp"] cwd = "/absolute/path/to/app" ``` Codex 对受信任的项目加载项目配置。编辑后打开或重启该项目,并确认服务工具可用。参见 OpenAI 官方文档的[项目配置](https://learn.chatgpt.com/docs/config-file/config-basic)与 [MCP 设置](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)。 ## 浏览、查看与安装 向助手明确指定 `@sui` 命名空间: > 在 @sui 中搜索表格组件,查看 @sui/data-table 及其依赖,然后在当前应用中安装它,并按我的别名配置适配文档示例。 服务提供以下工具完成此流程: | 工具 | 用途 | | --- | --- | | `get_project_registries` | 确认应用已配置 `@sui`。 | | `list_items_in_registries` | 列出 `@sui` 条目。 | | `search_items_in_registries` | 在 `@sui` 中搜索名称与描述。 | | `view_items_in_registries` | 查看条目的 manifest 和文件内容。 | | `get_add_command_for_items` | 返回所选条目的 CLI 安装命令。 | 例如,以 `registries: ["@sui"]` 和 `query: "table"` 搜索,再以 `items: ["@sui/data-table"]` 查看源码。拿到安装命令后,助手在消费应用目录执行。使用 Bun 时,对应命令为: ```bash bunx --bun shadcn@latest add @sui/data-table ``` 检查安装后的文件、样式与依赖。变更预览方式见 [CLI](/zh-CN/docs/cli),运行环境要求见[兼容性指南](/zh-CN/docs/compatibility)。 SUI 当前目录索引包含可安装的组件、业务区块、样式和工具,没有独立的 demo 或 example 条目。完整示例与 API 说明请使用 [LLMs 文档](/zh-CN/docs/llms-txt),不要期待 MCP 示例搜索返回 `@sui/*-demo` 条目。 ## 故障排查 | 现象 | 检查方式 | | --- | --- | | GitHub registry 返回 404 | 检查配置的地址、仓库访问权限,以及所需条目是否存在。 | | Unknown registry 或缺少 `@sui` | 检查消费应用 `components.json` 中的命名空间,再调用 `get_project_registries`。 | | 服务读取了其他项目 | 检查工作目录,必须指向包含 `components.json` 的消费应用;修改后重启服务。 | | 找不到 `bunx` | 确保客户端进程的 `PATH` 能找到 Bun,或在服务配置中使用 `bunx` 可执行文件的绝对路径。 | | 工具可用,但没有安装文件 | `get_add_command_for_items` 只返回命令,助手仍需在应用中执行它。 | 在与 MCP 相同的应用目录运行以下命令,可单独排查 registry 访问问题: ```bash bunx --bun shadcn@latest search @sui -q button bunx --bun shadcn@latest view @sui/button ``` 配置与条目说明见 [Registry](/zh-CN/docs/registry),安装与更新命令见 [CLI](/zh-CN/docs/cli)。 --- # Registry 了解 SUI 条目、共享样式和 GitHub 分发方式 页面: https://sui.draco.dev/zh-CN/docs/registry SUI registry 通过 [CLI](/zh-CN/docs/cli) 和 [MCP](/zh-CN/docs/mcp) 分发可编辑的组件源码。按照[安装指南](/zh-CN/docs/installation)配置 `@sui` 命名空间。 ## GitHub 文件 ```json { "registries": { "@sui": "https://raw.githubusercontent.com/draco-china/sui/main/registry/r/{name}.json" } } ``` `registry/r/registry.json` 是可搜索的目录索引,`registry/r/button.json` 等文件包含对应组件的安装内容。条目名称不带语言前缀,安装的组件使用英文默认文案,并接受应用提供的文本。 | 条目 | 内容 | | --- | --- | | `button` 等组件名称 | 组件、传递的本地依赖文件、包依赖、CSS 和主题 token | | `data-table`、`delete-resource`、`tanstack-form` | Block 及其所需源码和样式 | | `sui-style` | 共享主题与组件 CSS | | `sui-theme` | 主题工具与共享样式 | | `sui` | 全部组件和 Blocks | ## 文件与依赖 条目使用 shadcn registry schema。`dependencies` 和 `devDependencies` 声明包版本,`files` 包含源码及根据应用别名解析的目标路径,`css` 和 `cssVars` 合并到配置的样式文件。 每个条目自包含所需的本地 hooks、工具、类型声明和被引用组件。这样不会依赖消费应用的命名空间,也不会意外从其他 registry 安装同名组件。索引只描述条目,完整源码放在对应的安装文件中。 CLI 根据别名重写导入,文档工作区路径的替换方式见[导入映射](/zh-CN/docs/installation#import-installed-source)。安装后的主题值和源码可以修改;接入已有设计系统时先检查差异。 ## 更新已安装源码 通过 [CLI](/zh-CN/docs/cli#review-an-update) 预览变更后,再更新组件与共享依赖。安装属于源码复制,文件不会自动跟随组件库变更。保留应用的包管理器锁文件,并检查样式与本地修改。 LLM 文档由文档站的[纯文本接口](/zh-CN/docs/llms-txt)提供。Registry JSON 用于安装数据,MCP 客户端则单独启动 shadcn stdio 服务。 --- # 主题 语义颜色、明暗表面与可复用配色 页面: https://sui.draco.dev/zh-CN/docs/theming SUI 的颜色描述元素的用途。卡片使用 `card` 与 `card-foreground`,主要操作使用 `primary` 与 `primary-foreground`。切换配色或外观时,同一组件会随之适配。 共享 CSS 颜色变量统一使用 OKLCH,七套预设和自定义配色也输出 OKLCH;这是原颜色的等价转换,不改变配色。自定义输入和预设种子仍使用 HEX,对比度仍按 sRGB 相对亮度计算。共享包只提供通用语义变量,文档应用直接使用 `accent-foreground` 和 `ring` 表达链接与焦点。 ## 语义颜色的使用 先按用途选择变量,再将表面与对应的前景色配对使用。如果组件的内置变体已经表达该用途,优先使用变体。 **推荐** ```tsx import { Button } from "@workspace/ui/components/button";

修改已准备就绪。

; ``` **产品界面中应避免** ```tsx

修改已准备就绪。

; ``` 插画或外部品牌素材可以使用固定颜色。产品表面、控件与正文应使用语义变量,使不同主题保持一致。SUI 没有添加禁止基础颜色的 lint 规则。 ## 表面层次 从页面画布开始,根据内容用途选择表面。默认主题采用 Apple 风格的灰色画布、白色表面与蓝色操作;深色模式使用深灰表面与更明亮的蓝色。字体继续使用 Inter | 用途 | 表面 | 对应正文 | 常见场景 | | --- | --- | --- | --- | | 页面 | `bg-background` | `text-foreground` | 应用画布 | | 卡片 | `bg-card` | `text-card-foreground` | 分组内容 | | 浮层 | `bg-popover` | `text-popover-foreground` | 菜单与浮层 | | 次要 | `bg-secondary` | `text-secondary-foreground` | 次要控件 | | 低强调 | `bg-muted` | `text-muted-foreground` | 辅助内容 | | 选中 | `bg-accent` | `text-accent-foreground` | 选中与悬停 | 表面名称表达用途,而非固定的亮度顺序。`card` 和 `popover` 可以拥有相同颜色,同时保留各自的语义。 ## 主色与状态 主要操作使用 `primary`,其文字使用 `primary-foreground`。选中或悬停项使用 `accent`。链接使用 `accent-foreground`;为了满足中性表面上的文字对比度,它可以与主色种子不同。 Toggle 与 ToggleGroup 的按下状态、Command 高亮、当前导航链接、选中的选项和菜单项、表格选中行、选项卡片以及日历范围都使用选中配色。Tabs、分页当前页、复选框和单选框等实色选中态使用 `primary` 与 `primary-foreground`。焦点指示器使用 `ring`。 危险操作和无效输入使用 `destructive`,该颜色独立于当前强调色。SUI 未定义单独的成功、警告或信息颜色变量;用明确的文字与图标表达状态,或由应用定义相应的语义变量。 ```tsx import { Button } from "@workspace/ui/components/button"; ; ``` ## 正文 将前景变量与相应表面配对。`foreground` 是默认正文颜色,`muted-foreground` 用于说明与次要标签。不要仅靠浅色表达禁用、无效或选中状态,还应保留可访问标签和控件的状态属性。 ## 边框与焦点 `border` 分隔表面。现有 Input 使用 `bg-input/50` 填充。`ring` 标识键盘焦点。自定义交互元素时,应保留清晰的焦点轮廓。 ```tsx 查看颜色 ; ``` ## 图表 数据系列使用 `chart-1` 至 `chart-5`。每套预设提供五个协调的颜色。这些颜色用于区分系列,请搭配图例、标签或图案,不要只靠色相区分数据。图表颜色不应替代正文或状态变量。 ## 变量目录 按用途浏览真实的默认变量。每一行展示工具类、明暗值,并提供复制变量、工具类或值的按钮。目录直接读取 `globals.css`,不维护另一份颜色表。 ### 示例: theming-semantic-colors ```tsx import { Button } from "@workspace/ui/components/button"; import { InlineCopyText } from "@workspace/ui/components/inline-copy-text"; import { useState } from "react"; import { previewTokens, tokenValue } from "../support/theming-tokens"; import type { ExampleProps } from "../types"; const groups = [ { en: "Surfaces", zh: "表面", tokens: [ ["background", "bg-background", "Page canvas", "页面画布"], ["card", "bg-card", "Card surface", "卡片表面"], ["popover", "bg-popover", "Floating surface", "浮层表面"], ["secondary", "bg-secondary", "Secondary control", "次要控件"], ["muted", "bg-muted", "Quiet surface", "低强调表面"], ["accent", "bg-accent", "Selection and hover", "选中与悬停"], ], }, { en: "Text", zh: "正文", tokens: [ ["foreground", "text-foreground", "Page text", "页面正文"], ["card-foreground", "text-card-foreground", "Text on card", "卡片正文"], [ "popover-foreground", "text-popover-foreground", "Text on floating surface", "浮层正文", ], [ "secondary-foreground", "text-secondary-foreground", "Text on secondary control", "次要控件正文", ], [ "muted-foreground", "text-muted-foreground", "Supporting text", "辅助文字", ], [ "accent-foreground", "text-accent-foreground", "Text on selection", "选中态正文", ], ], }, { en: "Primary", zh: "主色", tokens: [ ["primary", "bg-primary", "Primary action", "主要操作"], [ "primary-foreground", "text-primary-foreground", "Text on primary action", "主要操作正文", ], ], }, { en: "Borders and focus", zh: "边框与焦点", tokens: [ ["border", "border-border", "Surface boundary", "表面边界"], ["input", "bg-input/50", "Input fill", "输入控件填充"], ["ring", "ring-ring", "Keyboard focus", "键盘焦点"], ], }, { en: "Status", zh: "状态", tokens: [ [ "destructive", "text-destructive", "Destructive action or invalid input", "危险操作或无效输入", ], ], }, { en: "Charts", zh: "图表", tokens: [1, 2, 3, 4, 5].map((index) => [ `chart-${index}`, `fill-chart-${index}`, `Data series ${index}`, `数据系列 ${index}`, ]), }, { en: "Sidebar", zh: "侧栏", tokens: [ ["sidebar", "bg-sidebar", "Sidebar surface", "侧栏表面"], [ "sidebar-foreground", "text-sidebar-foreground", "Sidebar text", "侧栏正文", ], [ "sidebar-primary", "bg-sidebar-primary", "Sidebar primary action", "侧栏主要操作", ], [ "sidebar-primary-foreground", "text-sidebar-primary-foreground", "Text on sidebar action", "侧栏主要操作正文", ], [ "sidebar-accent", "bg-sidebar-accent", "Sidebar selected item", "侧栏选中项", ], [ "sidebar-accent-foreground", "text-sidebar-accent-foreground", "Selected sidebar text", "侧栏选中项正文", ], [ "sidebar-border", "border-sidebar-border", "Sidebar boundary", "侧栏边界", ], [ "sidebar-ring", "ring-sidebar-ring", "Sidebar keyboard focus", "侧栏键盘焦点", ], ], }, ]; export default function Example({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const stateLabels = zh ? { dark: "深色", light: "浅色", } : { dark: "Dark", light: "Light", }; const [selected, setSelected] = useState(0); const group = groups[selected] ?? groups[0]; const labels = { copy: zh ? "复制" : "Copy", copied: zh ? "已复制" : "Copied", failed: zh ? "复制失败,请手动复制" : "Copy failed. Copy the text manually.", }; return (
{zh ? "颜色用途" : "Color roles"} {groups.map((item, index) => ( ))}
); } ``` ## 外观模式 在父节点添加 `dark` 即可使用深色变量,否则使用浅色变量。模式与配色是独立的选择:`dark` 控制外观,`data-color` 选择配色。 ```html ``` 下面两块面板使用相同的 SUI 组件,分别隔离默认明暗变量。可以编辑输入框、切换开关或保存设置,体验控件的表现。 ### 示例: theming-mode-comparison ```tsx import { cn } from "cn"; import { ThemePreview } from "../support/theming-preview"; import { previewStyle } from "../support/theming-tokens"; import type { ExampleProps } from "../types"; export default function Example({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const stateLabels = zh ? { dark: "深色", light: "浅色", } : { dark: "Dark", light: "Light", }; return (
{[false, true].map((dark) => (

{dark ? stateLabels.dark : stateLabels.light}

))}
); } ``` 要改变整个文档站,在外观面板选择**浅色**、**深色**或**跟随系统**。跟随系统是默认选项,会同步设备偏好。 ## 预设配色 选择**默认**或七套预设:竹青 Bamboo、烟紫 Mauve、雾蓝 Mist、沙金 Sand、松绿 Pine、绯红 Rose 和青柠 Lime。默认配色使用 `globals.css` 中的基础变量。每套预设都有完整的五色 palette,按顺序映射到 `chart-1` 至 `chart-5`;主色种子独立控制 `primary`,不必是第一个图表颜色。 下面为每套主题提供独立的完整卡片,展示五色 palette、可复制的颜色值和相同的交互组件预览。统一的明暗切换可在相同外观下比较各套配色,默认主题也有自己的完整卡片。 切换强调色时,表面、正文与危险颜色保持各自的用途。主要操作、选中态、焦点环、侧栏强调与图表跟随当前配色。为保持对比度,可访问的前景与焦点颜色可能不同于种子色。这些预览仅作用于局部,不会改变整站主题或浏览器存储。 ### 示例: theming-palette-playground ```tsx import { Button } from "@workspace/ui/components/button"; import { InlineCopyText } from "@workspace/ui/components/inline-copy-text"; import { themePresets } from "@workspace/ui/lib/theme/theme"; import { cn } from "cn"; import { useState } from "react"; import { ThemePreview } from "../support/theming-preview"; import { previewStyle, previewTokens, tokenValue, } from "../support/theming-tokens"; import type { ExampleProps } from "../types"; const palettes = [ { id: "default" as const, en: "Default", zh: "默认" }, ...themePresets, ]; export default function Example({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [dark, setDark] = useState(false); const mode = zh ? { dark: "深色预览", light: "浅色预览" } : { dark: "Dark preview", light: "Light preview" }; return (

{zh ? "每套配色包含五个协调色" : "Each palette contains five coordinated colors"}

{palettes.map((preset) => { const tokens = previewTokens(preset.id, dark); const colors = "palette" in preset ? preset.palette : [1, 2, 3, 4, 5].map((index) => tokenValue(tokens, `--chart-${index}`), ); return (

{zh ? preset.zh : preset.en}

{"color" in preset && ( {preset.color} )}
{colors.map((color, index) => (
))}
); })}

{zh ? "明暗切换仅作用于这些预览,不改变整站主题或浏览器存储" : "Appearance switches affect these previews without changing the site theme or browser storage."}

); } ``` 青柠 Lime 使用明亮的 `#D5F267` 主色与黑色文字。浅色模式的链接与焦点环使用更深的橄榄绿色,深色模式则保留明亮青柠色与中性深色表面的对比。在主题面板选择**青柠 Lime**,或设置 `data-color="lime"` 即可启用。 ## 自定义 HEX 预览和整站主题面板接受三位或六位 HEX,可以包含或省略 `#`。简写会展开为六位。无效输入显示错误,并保留当前有效主题。 主色种子保持原值。共享计算函数选择黑色或白色的主色文字,并针对明暗表面调整链接、选中态文字和焦点颜色。普通文字按 4.5:1、焦点颜色按 3:1 检查对比度。请使用对应的前景变量,不要假定白色文字适合每一种主色。 ### 示例: theming-custom-color ```tsx import { Button } from "@workspace/ui/components/button"; import { ColorPicker } from "@workspace/ui/components/color-picker"; import { Input } from "@workspace/ui/components/input"; import { Label } from "@workspace/ui/components/label"; import { normalizeHex } from "@workspace/ui/lib/theme/theme"; import { cn } from "cn"; import { useId, useState } from "react"; import { colorPickerLabels } from "../../lib/color-picker-labels"; import { ThemePreview } from "../support/theming-preview"; import { previewStyle } from "../support/theming-tokens"; import type { ExampleProps } from "../types"; export default function Example({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const id = useId(); const [dark, setDark] = useState(false); const [input, setInput] = useState("#0066CC"); const [custom, setCustom] = useState("#0066CC"); const [invalid, setInvalid] = useState(false); const mode = zh ? { dark: "深色预览", light: "浅色预览" } : { dark: "Dark preview", light: "Light preview" }; return (
{ event.preventDefault(); const next = normalizeHex(input); setInvalid(next === null); if (next) { setCustom(next); setInput(next); } }} >
setInput(event.target.value)} aria-invalid={invalid} aria-describedby={invalid ? `${id}-error` : undefined} />
{ setCustom(next); setInput(next); setInvalid(false); }} labels={colorPickerLabels(locale)} /> {invalid && ( )}
); } ``` ## 共享主题文件 导入 UI 样式即可包含默认变量、组件样式和全部七套预设: ```css @import "@workspace/ui/globals.css"; ``` 七个文件位于 `packages/ui/src/styles/themes`:`bamboo.css`、`mauve.css`、`mist.css`、`sand.css`、`pine.css`、`rose.css` 和 `lime.css`。已经加载 SUI 基础变量的应用可以只导入所需预设: ```css @import "@workspace/ui/themes/pine.css"; ``` 通过根节点或容器上的 `data-color="pine"` 选择配色。深色祖先节点会启用深色配色。重置根节点时,使用 `data-color="default"` 或移除该属性,并清理自定义行内变量。默认变量直接来自 `globals.css`,没有单独的默认主题文件。不含自身覆盖变量的嵌套容器会继承父节点的颜色。 ## 自定义应用主题 自定义输入使用共享辅助函数。先校验,再清理旧的行内强调色变量,仅为自定义种子计算覆盖值: ```tsx import { createThemeTokens, getThemeId, normalizeHex, themeTokenNames, } from "@workspace/ui/lib/theme/theme"; function applyTheme(input: string | null, dark: boolean) { const seed = input === null ? null : normalizeHex(input); if (input !== null && seed === null) return; const root = document.documentElement; root.classList.toggle("dark", dark); root.dataset.color = getThemeId(seed); for (const name of themeTokenNames) root.style.removeProperty(name); if (root.dataset.color === "custom") { for (const [name, value] of Object.entries(createThemeTokens(seed, dark))) { root.style.setProperty(name, value); } } } ``` 传入 `null` 恢复默认配色。外观变化时重新计算自定义覆盖变量。共享包负责配色和计算;模式选择、持久化与首次恢复由应用管理。可访问的链接使用 `text-accent-foreground`,焦点使用 `outline-ring`。文档样式将 Fumadocs 的 `--color-fd-*` 变量映射为共享语义变量;这些框架别名不属于共享主题文件或 `createThemeTokens` 的输出。 ## 创建配色 要添加可复用预设,在 `packages/ui/src/lib/theme/theme.ts` 的 `themePresets` 中加入稳定 ID、中英文名称、种子色与五个图表颜色。生成器会将该定义与 `globals.css` 当前的基础语义变量合并。 ```sh bun run --cwd packages/ui themes:generate ``` 将生成的单个文件导入 `globals.css`,再通过 `data-color` 使用该 ID。更改预设或基础变量后重新生成。采用新配色前,检查明暗模式、对应前景色、键盘焦点和图表图例。 ## 设置恢复 顶栏的“外观模式”和“强调色”是两个独立入口:前者选择浅色、深色或跟随系统,后者选择预设配色或自定义颜色。更改模式不会重置强调色。 文档站将选定的模式与强调色保存在浏览器中,首次绘制前恢复;跟随系统模式会实时响应设备变化。存储不可用时使用跟随系统与默认配色。本页交互示例不会写入这些设置。 ## 从右到左的界面 使用 `DirectionProvider` 包裹相关组件树,并优先使用逻辑方向间距工具类: ```tsx import { DirectionProvider } from "@workspace/ui/components/direction"; ; ``` --- # 兼容性 应用职责、SSR、Worker 加载和浏览器能力 页面: https://sui.draco.dev/zh-CN/docs/compatibility SUI 使用 React 19、Tailwind CSS 4 和 Base UI 基础交互。源码安装复用应用的别名和 CSS 入口,详见[安装指南](/zh-CN/docs/installation)。视觉体系使用 OKLCH 颜色、背景滤镜等现代 CSS 特性。 ## SSR 与应用状态 文档站使用 TanStack Start 和 SSR。依赖浏览器的 Viewer 和编辑器在挂载后初始化,加载期间显示基础内容或骨架。应用若区分服务端与客户端组件,应将交互组件放在客户端边界内,保留安装文件中的 `use client` 声明。 主题持久化、语言路由、校验和业务请求由应用负责。ThemeToggle 和 LocaleToggle 接受受控值与回调。Blocks 提供英文默认文案,通过 `labels` 接收应用语言系统的翻译。标签、描述和校验信息使用 [Field](/zh-CN/docs/components/field)组合。 ## Editor Workers [Editor](/zh-CN/docs/components/editor) 在本地加载 Monaco 与 Workers,安装源码使用兼容 Vite 的 `?worker` 导入。其他构建工具需要提供等价的 Worker 接入,安装源码不会自动配置该构建工具。将安装的 `worker.d.ts` 包含在 TypeScript 的源码范围中。 高亮失败时,CodeViewer 和 DiffViewer 仍可展示并复制原始内容。编辑器与 Viewer 复用基于 Shiki 的高亮基础。 ## 浏览器能力 | 功能 | 应用注意事项 | | --- | --- | | 复制 | 是否可用由浏览器权限与安全上下文决定,组件提供失败反馈 | | 全屏 | 浏览器全屏是可选能力,权限或平台限制可能阻止进入 | | Glass | 默认使用 CSS + SVG,可选 WebGPU 增强在能力不足或取样失败时降级 | | 主题转场 | 切换组件遵循减少动态效果设置,也可直接更新主题 | | HTML 预览 | HtmlViewer 默认允许脚本且不允许同源权限,Editor 内置 HTML 预览禁用脚本 | 跨域图片、iframe、视频、Canvas 与取样频率见 [Glass 限制](/zh-CN/docs/components/glass#limits)。透明表面应在实际背景上保持可读,必要时使用普通表面或更浓的材质。 ## 无障碍与方向 为纯图标操作组合可见标签或无障碍名称。弹窗保留标题,图片提供描述,加载与反馈文案从应用传入。基础交互提供键盘和焦点行为,替换原生控件的自定义结构可能改变这些行为。 使用 [Direction](/zh-CN/docs/components/direction)提供 RTL 上下文,并保持应用的 `dir` 属性一致。Loader 和主题转场遵循减少动态效果偏好,应用新增动画也应考虑该设置。 ## 验证 安装后运行消费应用的类型检查和生产构建,检查键盘流程、表单、浮层、复制反馈、主题和翻译文案。SUI 文档示例用于验证组件,不会替应用验证路由、持久化、业务请求或部署。 --- # 安装 通过 shadcn registry 安装 SUI 源码 页面: https://sui.draco.dev/zh-CN/docs/installation 通过 shadcn registry 将 SUI 源码安装到应用中,组件、样式和依赖根据应用配置接入。 ## 通过 registry 安装 ### 准备消费应用 使用 React 19、Tailwind CSS 4,以及配置为 Base UI 的 shadcn 项目。如果现有应用尚未初始化 shadcn,运行: ```bash bunx --bun shadcn@latest init --base base ``` 将以下条目合并到应用现有的 `components.json`,保留其 CSS 路径与别名: ```json { "registries": { "@sui": "https://raw.githubusercontent.com/draco-china/sui/main/registry/r/{name}.json" } } ``` 目录索引地址为 `https://raw.githubusercontent.com/draco-china/sui/main/registry/r/registry.json`,单个条目也位于该目录。 ### 搜索、查看与安装 在消费应用目录运行: ```bash bunx --bun shadcn@latest search @sui -q button bunx --bun shadcn@latest view @sui/button bunx --bun shadcn@latest add @sui/button @sui/editor ``` 按 registry 名称安装业务区块: ```bash bunx --bun shadcn@latest add @sui/data-table @sui/delete-resource @sui/tanstack-form ``` 安装完整集合,包括所有组件与业务区块: ```bash bunx --bun shadcn@latest add @sui/sui ``` 每个条目包含所需的本地组件依赖、hooks、工具、类型、包依赖,以及 SUI CSS 和主题 token。CLI 将源码安装到别名配置的目录,并将样式合并到配置的 Tailwind CSS 文件。独立条目 `@sui/sui-style` 和 `@sui/sui-theme` 提供共享样式与主题工具。 如果应用已有同名组件或自定义主题值,安装后检查生成的文件与 CSS。更新前可先预览: ```bash bunx --bun shadcn@latest add @sui/button --dry-run bunx --bun shadcn@latest add @sui/button --diff ``` Editor 使用本地打包的 Monaco workers,需要兼容 Vite 的 `?worker` 加载器。将安装的 `worker.d.ts` 保留在 TypeScript 的 include 范围内。安装完整集合时同样需要满足此条件。 Blocks 默认使用英文。通过应用语言配置传入文案,可定制界面文本,参见 [Data Table](/zh-CN/docs/blocks/data-table)、[Delete Resource](/zh-CN/docs/blocks/delete-resource) 和 [TanStack Form](/zh-CN/docs/blocks/tanstack-form) 文档。 ### 导入安装后的源码 使用默认 shadcn 别名时,从应用自身目录导入: ```tsx import { Button } from "@/components/ui/button"; export function SaveButton() { return ; } ``` 如果 `components.json` 中的别名不同,请使用实际配置。本文档的示例使用 `@workspace/ui/components/button` 等工作区路径。通过 registry 安装时,将 `@workspace/ui/components/*` 替换为 `ui` 别名,将 `@workspace/ui/blocks/*` 替换为 `components` 别名加 `/blocks/*`,将 hook 或工具导入替换为 `hooks` 或 `lib` 别名。例如,默认别名使用 `@/components/ui/button`、`@/components/blocks/data-table` 和 `@/hooks/use-mobile`。CLI 会自动适配安装文件内部的导入路径。 ## 连接 shadcn MCP 服务 [MCP 指南](/zh-CN/docs/mcp)介绍客户端配置、项目工作目录、请求示例和常见问题。MCP 复用应用的 `@sui` registry 配置。读取 API 文档和完整示例文本时使用 [LLMs](/zh-CN/docs/llms-txt)。 CSS 变量与颜色调整方式见[主题指南](/zh-CN/docs/theming),运行环境要求见[兼容性](/zh-CN/docs/compatibility)。 --- # 简介 从一个组件开始,构建属于你的界面 页面: https://sui.draco.dev/zh-CN/docs SUI 是基于 Base UI 与 Tailwind CSS 4 构建的可组合 React 组件集合,为表单、导航、反馈和内容展示提供一致的视觉基础。源码保存在 `packages/ui` 中,你可以直接阅读、修改,并按照应用需求自由组合。 ## 从这里开始 1. **接入应用。** 按照[安装指南](/zh-CN/docs/installation)添加工作区依赖、加载共享样式,并配置 Tailwind 扫描组件源码。 2. **试用组件。** 浏览[组件目录](/zh-CN/docs/components),每个页面都提供交互预览和完整示例源码。 3. **调整风格。** 通过[主题指南](/zh-CN/docs/theming)设置颜色与 CSS 变量,参考[设计指南](/zh-CN/docs/design-guidelines)统一字体、间距、表面和动效。 SUI 当前通过 Bun 工作区中的私有包 `@workspace/ui` 使用,尚未发布 SUI registry 或独立安装命令。复制示例前,请先完成安装指南中的配置。 ## 第一个组件 完成接入后,从组件各自的模块导入: ```tsx import { Button } from "@workspace/ui/components/button"; export function SaveButton() { return ; } ``` 文档预览与应用使用同一个共享包。代码标签展示包含导入和组合方式的完整示例文件,中英文页面共用这些示例。 ## 找到需要的组件 | 构建场景 | 推荐起点 | | --- | --- | | 表单与设置 | [Field](/zh-CN/docs/components/field)、[Input](/zh-CN/docs/components/input)、[Select](/zh-CN/docs/components/select)、[Switch](/zh-CN/docs/components/switch) | | 导航与反馈 | [Tabs](/zh-CN/docs/components/tabs)、[Dialog](/zh-CN/docs/components/dialog)、[Toast](/zh-CN/docs/components/toast) | | 内容与对话 | [Message](/zh-CN/docs/components/message)、[Markdown Viewer](/zh-CN/docs/components/markdown-viewer)、[Code Viewer](/zh-CN/docs/components/code-viewer) | | 可选玻璃表面 | [Glass](/zh-CN/docs/components/glass) | ## 有目的地组合 组件通过具名子组件表达结构。对话框由触发器、内容、标题、说明和操作组成;表单字段由标签、控件、说明和错误信息组成。将关联部分放在一起,保持交互完整,也让每一步操作都有清晰的目的。 Base UI 底层组件提供键盘交互、焦点管理和 ARIA 语义。应用仍需补充明确的标签、有用的说明和易于理解的反馈。请使用键盘与辅助技术验证自定义组合。 ## 使用源码与文档 - `packages/ui` 保存组件、共享 Hook、CSS 变量和强调色主题。 - `apps/docs` 保存双语文档站与可运行示例。 - [CSS 工具](/zh-CN/docs/utils)提供 Scroll Fade、Shimmer 等共享效果。 使用编程助手时,可通过 [llms.txt](/zh-CN/llms.txt)获取文档索引,通过 [llms-full.txt](/zh-CN/llms-full.txt)读取完整参考内容与示例源码。各文档页也可以通过页面操作查看 Markdown。 --- # 设计规范 完整的排版、间距、表面与交互设计规则,包含每条规则的推荐和避免对照 页面: https://sui.draco.dev/zh-CN/docs/design-guidelines 组合 SUI 应用界面时,遵循下面的全部规则。每一条都提供可运行的视觉对照和可复制的代码。避免栏中的样式是教学用反例,请勿直接用于产品界面。 ## 共享样式与语义颜色 导入 `@workspace/ui/globals.css`,使用 `@workspace/ui/components/*` 中的组件。原生文字元素通过 Tailwind 类设置字号和字重。组件颜色使用 `bg-background`、`bg-card`、`text-foreground`、`text-muted-foreground`、`bg-primary`、`border-border` 与 `ring-ring` 等语义 token,危险操作保留独立的 `destructive` 语义。 默认外观为 Apple 风格的中性色背景与蓝色强调色。共享配色通过 `data-color` 选择 `default`、`bamboo`、`mauve`、`mist`、`sand`、`pine` 或 `rose`。移除该属性或设置 `default` 可恢复默认外观;`dark` 控制暗色模式。配色只调整强调色、焦点和图表,背景与正文保持中性。具体配置见[主题](/zh-CN/docs/theming)。 ```css @import "@workspace/ui/globals.css"; ``` ```html ``` ## 正文统一使用 14px 所有内容文本,包括正文、按钮、数据以及其他交互控件中的文字,都必须使用 14px。16px 及以上字号仅用于标题和副标题。 ### 示例: design-guidelines-content-text-size ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return

{zh ? "内容文字" : "Content text"}

; } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return

{zh ? "内容文字" : "Content text"}

; } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx

Content text

; ``` **避免** ```tsx

Content text

; ``` ## 标题使用句首大写 英文标题使用句首大写,不要把每个单词的首字母都大写,也不要把整个标题转换为大写。产品名称保留其规范的大小写。中文标题按自然语言书写。 避免栏同时展示逐词首字母大写,以及使用 `uppercase` 转换为全大写的情况。 ### 示例: design-guidelines-heading-case ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample() { return

Recent requests

; } function AvoidSample() { return (

Recent Requests

Recent requests

); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx

Recent requests

; ``` **避免** ```tsx

Recent Requests

; ``` ```tsx

Recent requests

; ``` ## 不要修改字间距 不要使用 `tracking-*` 类修改字符间距。保留字体本身的字间距。 ### 示例: design-guidelines-font-tracking ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "项目指标" : "Project metrics"}

); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "项目指标" : "Project metrics"}

); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx

Project metrics

; ``` **避免** ```tsx

Project metrics

; ``` ## 不要使用 font-bold 标题使用 `font-semibold`,行内强调使用 `font-medium`。不要使用 `font-bold`。 ### 示例: design-guidelines-font-weight ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( <>

{zh ? "账户设置" : "Account settings"}

{zh ? "必填" : "required"} ); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( <>

{zh ? "账户设置" : "Account settings"}

{zh ? "必填" : "required"} ); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx <>

Account settings

required ; ``` **避免** ```tsx <>

Account settings

required ; ``` ## 相关文字放得更近 相关文字之间的间距,应小于这一组文字与外部内容之间的间距。 ### 示例: design-guidelines-related-text-spacing ```tsx import { Button } from "@workspace/ui/components/button"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "网站分析" : "Web analytics"}

{zh ? "无需修改代码即可衡量网站访问量" : "Measure site traffic without changing your code."}

); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "网站分析" : "Web analytics"}

{zh ? "无需修改代码即可衡量网站访问量" : "Measure site traffic without changing your code."}

); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Button } from "@workspace/ui/components/button";

Web analytics

Measure site traffic without changing your code.

; ``` **避免** ```tsx import { Button } from "@workspace/ui/components/button";

Web analytics

Measure site traffic without changing your code.

; ``` ## 根据文字行高调整留白 文字周围的留白需要考虑行高。通常,垂直方向的留白应略小于水平方向。 ### 示例: design-guidelines-text-spacing ```tsx import { Card } from "@workspace/ui/components/card"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return {zh ? "内容文字" : "Content text"}; } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return {zh ? "内容文字" : "Content text"}; } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Card } from "@workspace/ui/components/card"; Content text; ``` **避免** ```tsx import { Card } from "@workspace/ui/components/card"; Content text; ``` ## 悬停颜色不要使用过渡动画 悬停状态的颜色必须立即变化。快速交互上的过渡动画会让界面显得迟缓。 将指针分别移到两个按钮上,对比响应速度。SUI 按钮可以对 `transform` 和 `box-shadow` 使用过渡,悬停颜色仍然立即变化。 ### 示例: design-guidelines-hover-color-transitions ```tsx import { Button } from "@workspace/ui/components/button"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( ); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( ); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Button } from "@workspace/ui/components/button"; ; ``` **避免** ```tsx import { Button } from "@workspace/ui/components/button"; ; ``` ## 阴影表面不要叠加 border 使用 `ring-1 ring-border` 创建保持边缘清晰、不会挤占布局空间的轮廓。同一表面不要同时使用 `border` 和投影。 SUI Card 已自带阴影和细轮廓。避免示例明确关闭其默认 ring,展示 border 与阴影叠加的情况。 ### 示例: design-guidelines-shadow-borders ```tsx import { Card } from "@workspace/ui/components/card"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( {zh ? "内容文字" : "Content text"} ); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return ( {zh ? "内容文字" : "Content text"} ); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Card } from "@workspace/ui/components/card"; Content text; ``` **避免** ```tsx import { Card } from "@workspace/ui/components/card"; Content text ; ``` ## 嵌套圆角保持同心 当两层边框或 ring 之间的距离不超过 8px 时,圆角半径必须在数学上保持同心:外层半径 = 内层半径 + 内边距。 SUI 默认圆角下,`rounded-lg` 为 10px,`rounded-xl` 为 14px,`p-1` 为 4px。圆角阶梯采用固定增量:`xl`、`2xl`、`3xl`、`4xl` 分别在基准圆角上增加 `0.25rem`、`0.5rem`、`0.75rem`、`1rem`,修改 `--radius` 后仍保持这些差值。`sm` 与 `md` 分别减去 `0.25rem`、`0.125rem`,最小为零。 内层使用 `rounded-lg` 时,外层的 `rounded-xl` 配合 `p-1`,`rounded-2xl` 配合 `p-2`。其他内边距应按“内层半径 + 内边距”计算外层半径;圆角阶梯不会让任意组合自动同心。切换下方基准圆角,观察两种写法的区别。 外层如果使用 `border`,两层盒子的实际距离还要包含边框宽度;`ring` 不占用布局空间。 ### 示例: design-guidelines-concentric-border-radius ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (
{zh ? "内容文字" : "Content text"}
); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (
{zh ? "内容文字" : "Content text"}
); } export default function Example({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [radius, setRadius] = useState(10); return (
{zh ? "基准圆角" : "Base radius"} {[0, 4, 10, 16].map((value) => ( ))}
} avoid={} />
); } import { Button } from "@workspace/ui/components/button"; import { type CSSProperties, useState } from "react"; ``` **推荐** ```tsx
Content text
; ``` **避免** ```tsx
Content text
; ``` ## 图标与第一行文字对齐 行内图标在视觉上应与文字大小相当,并与文字垂直居中。多行文本使用 `h-lh flex items-center`,让图标对齐第一行。 避免栏包含两种情况:图标外层缺少行高,以及图标与整段文字居中。装饰性图标使用 `aria-hidden`;只有图标的交互按钮需要可访问名称。 ### 示例: design-guidelines-icon-alignment ```tsx import { InfoIcon } from "lucide-react"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "这段文字可能会换行,但图标仍应与第一行保持对齐" : "Text that may wrap onto multiple lines and still align with the icon."}

); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "这段文字可能会换行,但图标仍应与第一行保持对齐" : "Text that may wrap onto multiple lines and still align with the icon."}

); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { InfoIcon } from "lucide-react";

Text that may wrap onto multiple lines and still align with the icon.

; ``` **避免** ```tsx import { InfoIcon } from "lucide-react";

Text that may wrap onto multiple lines and still align with the icon.

; ``` ```tsx import { InfoIcon } from "lucide-react";
; ``` ## 行内等宽文字略微缩小 等宽文字与普通文字混排时,字号应略小,约为周围文字的 `0.9em`。 ### 示例: design-guidelines-inline-monospace-size ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "编辑 " : "Edit "} config.ts {zh ? " 后继续" : " to continue."}

); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "编辑 " : "Edit "} config.ts {zh ? " 后继续" : " to continue."}

); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx

Edit config.ts to continue.

; ``` **避免** ```tsx

Edit config.ts to continue.

; ``` ## 使用 border 分隔吸顶元素 使用 `border` 将吸顶元素与下面滚动的内容分隔开。 滚动两栏,观察吸顶标题与下方内容的边界。这里的标题不带投影,因此使用分隔 border。 ### 示例: design-guidelines-sticky-borders ```tsx import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (
{zh ? "最近请求" : "Recent requests"}
{Array.from({ length: 8 }, (_, index) => ({ id: `request-${index + 1}`, number: index + 1, })).map((request) => (

{zh ? "请求" : "Request"} {request.number}

))}
); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (
{zh ? "最近请求" : "Recent requests"}
{Array.from({ length: 8 }, (_, index) => ({ id: `request-${index + 1}`, number: index + 1, })).map((request) => (

{zh ? "请求" : "Request"} {request.number}

))}
); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx
Recent requests
{Array.from({ length: 8 }, (_, index) => ({ id: `request-${index + 1}`, number: index + 1, })).map((request) => (

Request {request.number}

))}
; ``` **避免** ```tsx
Recent requests
{Array.from({ length: 8 }, (_, index) => ({ id: `request-${index + 1}`, number: index + 1, })).map((request) => (

Request {request.number}

))}
; ``` ## 折叠动画期间保持内容尺寸 可折叠内容在关闭过程中必须保持自身尺寸,避免内容在动画期间移动或重新排版。 分别切换两栏面板并观察段落。外层宽度从 256px 动画变为 0,推荐示例的内层保持 `w-64`;避免示例会同步缩小文字容器,导致换行变化。减少动态效果时不使用过渡。 ### 示例: design-guidelines-collapse-content-size ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { useReducedMotion } from "@workspace/ui/hooks/use-reduced-motion"; import { motion } from "motion/react"; import { useState } from "react"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [open, setOpen] = useState(true); const reduce = useReducedMotion(); return (
{zh ? "面板关闭时,这段文字应保持相同的换行,不要在动画过程中重新排版" : "Text should keep the same line breaks while this panel closes."}
); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [open, setOpen] = useState(true); const reduce = useReducedMotion(); return (
{zh ? "面板关闭时,这段文字应保持相同的换行,不要在动画过程中重新排版" : "Text should keep the same line breaks while this panel closes."}
); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Button } from "@workspace/ui/components/button"; import { useReducedMotion } from "@workspace/ui/hooks/use-reduced-motion"; import { motion } from "motion/react"; import { useState } from "react"; export function RecommendedExample() { const [open, setOpen] = useState(true); const reduce = useReducedMotion(); return (
Text should keep the same line breaks while this panel closes.
); } ``` **避免** ```tsx import { Button } from "@workspace/ui/components/button"; import { useReducedMotion } from "@workspace/ui/hooks/use-reduced-motion"; import { motion } from "motion/react"; import { useState } from "react"; export function AvoidExample() { const [open, setOpen] = useState(true); const reduce = useReducedMotion(); return (
Text should keep the same line breaks while this panel closes.
); } ``` ## 不要嵌套有阴影的卡片 不要将有阴影的卡片再叠放在另一张有阴影的卡片上。 卡片内部使用普通容器、间距或分隔线组织内容。推荐示例将标题放在有阴影的表面之外。 ### 示例: design-guidelines-layer-card-nesting ```tsx import { Card } from "@workspace/ui/components/card"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "最近请求" : "Recent requests"}

{zh ? "请求数据" : "Request data"}
); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; return (

{zh ? "最近请求" : "Recent requests"}

{zh ? "请求数据" : "Request data"}
); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Card } from "@workspace/ui/components/card";

Recent requests

Request data
; ``` **避免** ```tsx import { Card } from "@workspace/ui/components/card";

Recent requests

Request data
; ``` ## 不要根据 open 条件挂载弹窗 根据条件挂载或卸载弹窗,会让打开和关闭动画无法正常完成。使用 `open` 属性控制弹窗是否可见。 分别打开并关闭两栏弹窗。推荐示例的 `Dialog` 始终保留在 React 树中,由 Base UI 管理弹出层是否挂载、关闭动画、焦点和关闭交互。避免示例在 `open` 变为 false 时立即删除整棵弹窗树。始终提供标题和描述。 ### 示例: design-guidelines-dialog-rendering ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@workspace/ui/components/dialog"; import { useState } from "react"; import { DesignComparison } from "../design-guidelines-comparison"; import type { ExampleProps } from "../types"; function RecommendedSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [open, setOpen] = useState(false); return ( }> {zh ? "打开弹窗" : "Open dialog"} {zh ? "编辑项目" : "Edit project"} {zh ? "更新此项目的设置" : "Update this project’s settings."} }> {zh ? "关闭" : "Close"} ); } function AvoidSample({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const [open, setOpen] = useState(false); return ( <> {open && ( {zh ? "编辑项目" : "Edit project"} {zh ? "更新此项目的设置" : "Update this project’s settings."} }> {zh ? "关闭" : "Close"} )} ); } export default function Example({ locale }: ExampleProps) { return ( } avoid={} /> ); } ``` **推荐** ```tsx import { Button } from "@workspace/ui/components/button"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@workspace/ui/components/dialog"; import { useState } from "react"; export function RecommendedExample() { const [open, setOpen] = useState(false); return ( }> Open dialog Edit project Update this project’s settings. }>Close ); } ``` **避免** ```tsx import { Button } from "@workspace/ui/components/button"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@workspace/ui/components/dialog"; import { useState } from "react"; export function AvoidExample() { const [open, setOpen] = useState(false); return ( <> {open && ( Edit project Update this project’s settings. }> Close )} ); } ``` --- # 组件 按英文名称字母顺序浏览全部 67 个 SUI 组件 页面: https://sui.draco.dev/zh-CN/docs/components 所有预览均使用当前工作区中的真实组件。选择组件查看导入方式、组合结构、示例与 API。 - [Accordion](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Faccordion): 一组垂直堆叠的交互式标题,每个标题显示一部分内容 - [Alert](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Falert): 显示标注以引起用户注意 - [Alert Dialog](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Falert-dialog): 模式对话框,用重要内容打断用户并期望得到响应 - [Aspect Ratio](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Faspect-ratio): 以所需的比例显示内容 - [Attachment](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fattachment): 显示带有媒体、元数据、上传状态和操作的文件或图像附件 - [Autocomplete](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fautocomplete): 根据输入提供匹配建议,同时允许用户输入自定义文本 - [Avatar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Favatar): 具有代表用户的备用图像元素 - [Badge](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fbadge): 显示徽章或看起来像徽章的组件 - [Breadcrumb](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fbreadcrumb): 使用链接层次结构显示当前资源的路径 - [Bubble](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fbubble): 在消息气泡中显示对话内容。支持变体、对齐、分组、回应和可折叠内容 - [Button](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fbutton): 显示按钮或看起来像按钮的组件 - [Button Group](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fbutton-group): 将相关按钮分组在一起并具有一致样式的容器 - [Calendar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcalendar): 允许用户选择日期或日期范围的日历组件 - [Card](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcard): 显示带有页眉、内容和页脚的卡片 - [Carousel](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcarousel): 使用 Embla 构建的具有运动和滑动功能的轮播 - [Chart](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fchart): 漂亮的图表。使用 Recharts 构建。复制并粘贴到您的应用程序中 - [Checkbox](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcheckbox): 允许用户在选中和不选中之间切换的控件 - [Clipboard Text](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fclipboard-text): 可选择的文本字段,配备独立复制按钮和异步反馈 - [Code Viewer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcode-viewer): 支持语法高亮、折叠、行高亮、复制和流式反馈的代码查看器 - [Collapsible](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcollapsible): 展开/折叠面板的交互式组件 - [ColorPicker](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcolor-picker): 通过饱和度区域、色相和透明度滑条、颜色值与预设色板选择颜色 - [Combobox](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcombobox): 自动完成输入并包含建议列表 - [Command](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcommand): 用于搜索和快速操作的命令菜单 - [Context Menu](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fcontext-menu): 显示右键单击触发的操作菜单 - [Dialog](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fdialog): 覆盖在主窗口或另一个对话框窗口上的窗口,使下面的内容呈现inert 状态 - [Diff Viewer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fdiff-viewer): 支持并排与合并视图的逐行代码差异查看器 - [Direction](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fdirection): 为您的应用程序设置文本方向的提供程序组件 - [Drawer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fdrawer): React 的抽屉组件 - [Dropdown Menu](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fdropdown-menu): 向用户显示由按钮触发的菜单,例如一组操作或功能 - [Editor](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Feditor): 按需加载的代码编辑器,支持内容预览、工具栏操作与全屏模式 - [Empty](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fempty): 使用 Empty 组件显示空状态 - [Field](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ffield): 组合标签、控件和帮助文本以组成可访问的表单字段和分组输入 - [Glass](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fglass): 以 SVG 高光和 CSS 磨砂为基础,按能力通过 WebGPU 增强真实折射 - [Hover Card](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fhover-card): 供视力正常的用户预览链接后面可用的内容 - [HTML Viewer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fhtml-viewer): 使用独立 iframe 展示 HTML 内容的查看器 - [Image Viewer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fimage-viewer): 支持切换、缩放、旋转和平移的图片查看对话框 - [Inline Copy Text](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Finline-copy-text): 可读的行内代码,点击或键盘激活后复制其值 - [Input](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Finput): 用于表单和用户数据输入的文本输入组件,具有内置样式和辅助功能 - [Input Group](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Finput-group): 为输入框添加附加内容、按钮和辅助信息 - [Input OTP](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Finput-otp): 支持字符过滤与异步验证反馈的独立验证码输入框 - [Item](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fitem): 一个多功能组件,用于显示带有媒体、标题、描述和操作的内容 - [Kbd](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fkbd): 用于显示来自键盘的文本用户输入 - [Label](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Flabel): 呈现与控件关联的可访问标签 - [Loader](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Floader): 十八种加载动效,支持可访问文案、自定义速度及减少动态效果设置 - [Locale Toggle](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Flocale-toggle): 受控语言切换按钮或选择器,不预设路由与持久化方式 - [LongText](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Flong-text): 文本溢出时截断,并支持查看完整内容。 - [Markdown Viewer](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fmarkdown-viewer): 支持表格、任务列表、提示块、代码高亮和 HTML 清理的 Markdown 查看器 - [Marker](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fmarker): 显示对话中的内联状态、系统注释、边框行或带标签的分隔符 - [Menubar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fmenubar): 桌面应用程序中常见的视觉持久菜单,可提供对一组一致命令的快速访问 - [Message](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fmessage): 显示对话中的消息,带有可选的头像、页眉、页脚和对齐方式 - [Message Scroller](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fmessage-scroller): 聊天滚动容器:锚定对话轮次、恢复已保存的记录、跟随流式回复、稳定加载历史消息,并跳转至指定消息 - [Native Select](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fnative-select): 具有一致设计系统集成的样式化原生 HTML 选择元素 - [Navigation Menu](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fnavigation-menu): 用于导航网站的链接的集合 - [NavigationProgress](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fnavigation-progress): 通过受控状态显示延迟出现、完成收尾的页面加载进度条。 - [Pagination](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fpagination): 紧凑的数据分页,支持范围摘要、页码导航和每页条数选择 - [Popover](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fpopover): 通过按钮触发,在Portal 容器中显示富内容 - [Progress](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fprogress): 显示任务完成进度的指示器,通常显示为进度条 - [QRCode](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fqr-code): 支持主题适配、加载反馈和可选揭示动画的二维码组件 - [Questionnaire](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fquestionnaire): 多步骤调查问卷,包括单项选择、多项选择、自由形式和可跳过的问题 - [Radio Group](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fradio-group): 一组可检查按钮(称为单选按钮),一次只能检查一个按钮 - [Resizable](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fresizable): 通过键盘支持可访问可调整大小的面板组和布局 - [Scroll Area](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fscroll-area): 增强了自定义跨浏览器样式的原生滚动功能 - [Select](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fselect): 显示选项列表供用户选择(由按钮触发) - [Sensitive Input](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fsensitive-input): 使用固定遮罩的敏感内容输入框,支持点击揭示、键盘操作与复制反馈 - [Separator](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fseparator): 在视觉上或语义上分隔内容 - [Sheet](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fsheet): 扩展 Dialog 组件以显示补充屏幕主要内容的内容 - [Sidebar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fsidebar): 一个可组合、可主题化和可定制的侧边栏组件 - [Skeleton](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fskeleton): 用于在加载内容时显示占位符 - [Slider](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fslider): 用户从给定范围内选择一个值的输入 - [Switch](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Fswitch): 允许用户在选中和不选中之间切换的控件 - [TabBar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftab-bar): 支持受控应用导航、长按滑动选择和可选玻璃材质的 TabBar - [Table](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftable): 响应式表格组件 - [Tabs](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftabs): 一组分层的内容部分(称为选项卡面板),一次显示一个 - [Tag Input](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftag-input): 多值标签输入框,支持自由输入、建议选项与键盘操作 - [Textarea](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftextarea): 显示表单文本区域或看起来像文本区域的组件 - [Theme Toggle](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftheme-toggle): 支持可选视图过渡效果的受控明暗主题切换按钮 - [Toast](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftoast): 暂时显示的简洁消息 - [Toggle](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftoggle): 可以打开或关闭的两种状态按钮 - [Toggle Group](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftoggle-group): 一组可以打开或关闭的两种状态按钮 - [Toolbar](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftoolbar): 将相关操作与控件组合在一起,并提供方向键焦点导航 - [Tooltip](https://sui.draco.dev/api/llms-markdown?locale=zh-CN&slug=components%2Ftooltip): 当元素接收键盘焦点或鼠标悬停在其上时显示与该元素相关的信息的弹出窗口 --- # Accordion 一组垂直堆叠的交互式标题,每个标题显示一部分内容 页面: https://sui.draco.dev/zh-CN/docs/components/accordion ### 示例: accordion-demo ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; export default function AccordionDemo() { return ( What are your shipping options? We offer standard (5-7 days), express (2-3 days), and overnight shipping. Free shipping on international orders. What is your return policy? Returns accepted within 30 days. Items must be unused and in original packaging. Refunds processed within 5-7 business days. How can I contact customer support? Reach us via email, live chat, or phone. We respond within 24 hours during business days. ); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/accordion ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx showLineNumbers import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion" ``` ```tsx showLineNumbers Is it accessible? Yes. It adheres to the WAI-ARIA design pattern. ``` ## 组合结构 使用以下组合来构建 `Accordion`: ```text Accordion ├── AccordionItem │ ├── AccordionTrigger │ └── AccordionContent └── AccordionItem ├── AccordionTrigger └── AccordionContent ``` ## 基本用法 一次显示一个项目的基本Accordion。第一项默认打开。 ### 示例: accordion-basic ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; const items = [ { value: "item-1", trigger: "How do I reset my password?", content: "Click on 'Forgot Password' on the login page, enter your email address, and we'll send you a link to reset your password. The link will expire in 24 hours.", }, { value: "item-2", trigger: "Can I change my subscription plan?", content: "Yes, you can upgrade or downgrade your plan at any time from your account settings. Changes will be reflected in your next billing cycle.", }, { value: "item-3", trigger: "What payment methods do you accept?", content: "We accept all major credit cards, PayPal, and bank transfers. All payments are processed securely through our payment partners.", }, ]; export function AccordionBasic() { return ( {items.map((item) => ( {item.trigger} {item.content} ))} ); } export default AccordionBasic; ``` ## 同时展开多项 使用 `multiple` 属性允许同时打开多个项目。 ### 示例: accordion-multiple ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; const items = [ { value: "notifications", trigger: "Notification Settings", content: "Manage how you receive notifications. You can enable email alerts for updates or push notifications for mobile devices.", }, { value: "privacy", trigger: "Privacy & Security", content: "Control your privacy settings and security preferences. Enable two-factor authentication, manage connected devices, review active sessions, and configure data sharing preferences. You can also download your data or delete your account.", }, { value: "billing", trigger: "Billing & Subscription", content: "View your current plan, payment history, and upcoming invoices. Update your payment method, change your subscription tier, or cancel your subscription.", }, ]; export function AccordionMultiple() { return ( {items.map((item) => ( {item.trigger} {item.content} ))} ); } export default AccordionMultiple; ``` ## 禁用状态 使用 `AccordionItem` 上的 `disabled` 属性来禁用单个项目。 ### 示例: accordion-disabled ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; export default function AccordionDisabled() { return ( Can I access my account history? Yes, you can view your complete account history including all transactions, plan changes, and support tickets in the Account History section of your dashboard. Premium feature information This section contains information about premium features. Upgrade your plan to access this content. How do I update my email address? You can update your email address in your account settings. You'll receive a verification email at your new address to confirm the change. ); } ``` ## 边框 将 `border` 添加到 `Accordion`,将 `border-b last:border-b-0` 添加到 `AccordionItem`,以向项目添加边框。 ### 示例: accordion-borders ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; const items = [ { value: "billing", trigger: "How does billing work?", content: "We offer monthly and annual subscription plans. Billing is charged at the beginning of each cycle, and you can cancel anytime. All plans include automatic backups, 24/7 support, and unlimited team members.", }, { value: "security", trigger: "Is my data secure?", content: "Yes. We use end-to-end encryption, SOC 2 Type II compliance, and regular third-party security audits. All data is encrypted at rest and in transit using industry-standard protocols.", }, { value: "integration", trigger: "What integrations do you support?", content: "We integrate with 500+ popular tools including Slack, Zapier, Salesforce, HubSpot, and more. You can also build custom integrations using our REST API and webhooks.", }, ]; export default function AccordionBorders() { return ( {items.map((item) => ( {item.trigger} {item.content} ))} ); } ``` ## 卡片组合 将 `Accordion` 包裹在 `Card` 组件中。 ### 示例: accordion-card ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@workspace/ui/components/card"; const items = [ { value: "plans", trigger: "What subscription plans do you offer?", content: "We offer three subscription tiers: Starter ($9/month), Professional ($29/month), and Enterprise ($99/month). Each plan includes increasing storage limits, API access, priority support, and team collaboration features.", }, { value: "billing", trigger: "How does billing work?", content: "Billing occurs automatically at the start of each billing cycle. We accept all major credit cards, PayPal, and ACH transfers for enterprise customers. You'll receive an invoice via email after each payment.", }, { value: "cancel", trigger: "How do I cancel my subscription?", content: "You can cancel your subscription anytime from your account settings. There are no cancellation fees or penalties. Your access will continue until the end of your current billing period.", }, ]; export default function AccordionCard() { return ( Subscription & Billing Common questions about your account, plans, payments and cancellations. {items.map((item) => ( {item.trigger} {item.content} ))} ); } ``` ## 从右到左 关于 shadcn/ui 的 RTL 支持,参阅 [RTL 配置指南](https://ui.shadcn.com/docs/rtl)。 ### 示例: accordion-rtl ```tsx "use client"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@workspace/ui/components/accordion"; import { type Translations, useTranslation } from "./support"; const translations: Translations = { en: { dir: "ltr", values: { question1: "How do I reset my password?", answer1: "Click on 'Forgot Password' on the login page, enter your email address, and we'll send you a link to reset your password.", question2: "Can I change my subscription plan?", answer2: "Yes, you can upgrade or downgrade your plan at any time from your account settings. Changes will be reflected in your next billing cycle.", question3: "What payment methods do you accept?", answer3: "We accept all major credit cards, PayPal, and bank transfers. All payments are processed securely through our payment partners.", }, }, ar: { dir: "rtl", values: { question1: "كيف يمكنني إعادة تعيين كلمة المرور؟", answer1: "انقر على 'نسيت كلمة المرور' في صفحة تسجيل الدخول، أدخل عنوان بريدك الإلكتروني، وسنرسل لك رابطًا لإعادة تعيين كلمة المرور. سينتهي صلاحية الرابط خلال 24 ساعة.", question2: "هل يمكنني تغيير خطة الاشتراك الخاصة بي؟", answer2: "نعم، يمكنك ترقية أو تخفيض خطتك في أي وقت من إعدادات حسابك. ستظهر التغييرات في دورة الفوترة التالية.", question3: "ما هي طرق الدفع التي تقبلونها؟", answer3: "نقبل جميع بطاقات الائتمان الرئيسية و PayPal والتحويلات المصرفية. تتم معالجة جميع المدفوعات بأمان من خلال شركاء الدفع لدينا.", }, }, he: { dir: "rtl", values: { question1: "איך אני מאפס את הסיסמה שלי?", answer1: "לחץ על 'שכחתי סיסמה' בעמוד ההתחברות, הזן את כתובת האימייל שלך, ונשלח לך קישור לאיפוס הסיסמה. הקישור יפוג תוך 24 שעות.", question2: "האם אני יכול לשנות את תוכנית המנוי שלי?", answer2: "כן, אתה יכול לשדרג או להוריד את התוכנית שלך בכל עת מההגדרות של החשבון שלך. השינויים יבואו לידי ביטוי במחזור החיוב הבא.", question3: "אילו אמצעי תשלום אתם מקבלים?", answer3: "אנו מקבלים כרטיסי אשראי, PayPal והעברות בנקאיות.", }, }, }; const items = [ { value: "item-1", questionKey: "question1" as const, answerKey: "answer1" as const, }, { value: "item-2", questionKey: "question2" as const, answerKey: "answer2" as const, }, { value: "item-3", questionKey: "question3" as const, answerKey: "answer3" as const, }, ] as const; export function AccordionRtl() { const { t } = useTranslation(translations, "ar"); return ( {items.map((item) => ( {t[item.questionKey]} {t[item.answerKey]} ))} ); } export default AccordionRtl; ``` ## API 参考 有关更多信息,请参阅[Base UI](https://base-ui.com/react/components/accordion#api-reference) 文档。 - [Documentation](https://base-ui.com/react/components/accordion) - [API reference](https://base-ui.com/react/components/accordion#api-reference) --- # Alert 显示标注以引起用户注意 页面: https://sui.draco.dev/zh-CN/docs/components/alert ### 示例: alert-demo ```tsx import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { CheckCircle2Icon, InfoIcon } from "lucide-react"; export default function AlertDemo() { return (
Payment successful Your payment of $29.99 has been processed. A receipt has been sent to your email address. New feature available We've added dark mode support. You can enable it in your account settings.
); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/alert ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx showLineNumbers import { Alert, AlertAction, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert" ``` ```tsx showLineNumbers Heads up! You can add components and dependencies to your app using the cli. ``` ## 组合结构 使用以下组合来构建 `Alert`: ```text Alert ├── Icon ├── AlertTitle ├── AlertDescription └── AlertAction ``` ## 基本用法 带有图标、标题和说明的基本提示。 ### 示例: alert-basic ```tsx import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { CheckCircle2Icon } from "lucide-react"; export default function AlertBasic() { return ( Account updated successfully Your profile information has been saved. Changes will be reflected immediately. ); } ``` ## 危险操作 使用 `variant="destructive"` 创建危险提示。 ### 示例: alert-destructive ```tsx import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { AlertCircleIcon } from "lucide-react"; export default function AlertDestructive() { return ( Payment failed Your payment could not be processed. Please check your payment method and try again. ); } ``` ## 操作 使用 `AlertAction` 向提示添加按钮或其他操作元素。 ### 示例: alert-action ```tsx import { Alert, AlertAction, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { Button } from "@workspace/ui/components/button"; export default function AlertActionExample() { return ( Dark mode is now available Enable it under your profile settings to get started. ); } ``` ## 自定义颜色 您可以通过将自定义类(例如 `bg-amber-50 dark:bg-amber-950`)添加到 `Alert` 组件来自定义提示颜色。 ### 示例: alert-colors ```tsx import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { AlertTriangleIcon } from "lucide-react"; export default function AlertColors() { return ( Your subscription will expire in 3 days. Renew now to avoid service interruption or upgrade to a paid plan to continue using the service. ); } ``` ## 从右到左 关于 shadcn/ui 的 RTL 支持,参阅 [RTL 配置指南](https://ui.shadcn.com/docs/rtl)。 ### 示例: alert-rtl ```tsx "use client"; import { Alert, AlertDescription, AlertTitle, } from "@workspace/ui/components/alert"; import { CheckCircle2Icon, InfoIcon } from "lucide-react"; import { type Translations, useTranslation } from "./support"; const translations: Translations = { en: { dir: "ltr", values: { paymentTitle: "Payment successful", paymentDescription: "Your payment of $29.99 has been processed. A receipt has been sent to your email address.", featureTitle: "New feature available", featureDescription: "We've added dark mode support. You can enable it in your account settings.", }, }, ar: { dir: "rtl", values: { paymentTitle: "تم الدفع بنجاح", paymentDescription: "تمت معالجة دفعتك البالغة 29.99 دولارًا. تم إرسال إيصال إلى عنوان بريدك الإلكتروني.", featureTitle: "ميزة جديدة متاحة", featureDescription: "لقد أضفنا دعم الوضع الداكن. يمكنك تفعيله في إعدادات حسابك.", }, }, he: { dir: "rtl", values: { paymentTitle: "התשלום בוצע בהצלחה", paymentDescription: "התשלום שלך בסך 29.99 דולר עובד. קבלה נשלחה לכתובת האימייל שלך.", featureTitle: "תכונה חדשה זמינה", featureDescription: "הוספנו תמיכה במצב כהה. אתה יכול להפעיל אותו בהגדרות החשבון שלך.", }, }, }; const alerts = [ { icon: CheckCircle2Icon, titleKey: "paymentTitle" as const, descriptionKey: "paymentDescription" as const, }, { icon: InfoIcon, titleKey: "featureTitle" as const, descriptionKey: "featureDescription" as const, }, ] as const; export function AlertRtl() { const { dir, t } = useTranslation(translations, "ar"); return (
{alerts.map((alert) => { const Icon = alert.icon; return ( {t[alert.titleKey]} {t[alert.descriptionKey]} ); })}
); } export default AlertRtl; ``` ## API 参考 ### Alert `Alert` 组件显示标注以引起用户注意 | 属性 |类型 |默认 | | --------- | ---------------------------- | ----------- | | `variant` | `"default" \| "destructive"` | `"default"` | ### AlertTitle `AlertTitle` 组件显示提示的标题 | 属性 |类型 |默认 | | ----------- | -------- | -------- | | `className` | `string` | - | ### AlertDescription `AlertDescription` 组件显示提示的描述或内容 | 属性|类型 |默认 | | ----------- | -------- | -------- | | `className` | `string` | - | ### AlertAction `AlertAction` 组件显示一个绝对位于提示右上角的操作元素(如按钮) | 属性|类型 |默认 | | ----------- | -------- | -------- | | `className` | `string` | - | --- # Alert Dialog 模式对话框,用重要内容打断用户并期望得到响应 页面: https://sui.draco.dev/zh-CN/docs/components/alert-dialog ### 示例: alert-dialog-demo ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; export default function AlertDialogDemo() { return ( }> Show Dialog Are you absolutely sure? This action cannot be undone. This will permanently delete your account from our servers. Cancel Continue ); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/alert-dialog ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx showLineNumbers import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog" ``` ```tsx showLineNumbers }> Show Dialog Are you absolutely sure? This action cannot be undone. This will permanently delete your account from our servers. Cancel Continue ``` ## 组合结构 使用以下组合来构建 `AlertDialog`: ```text AlertDialog ├── AlertDialogTrigger └── AlertDialogContent ├── AlertDialogHeader │ ├── AlertDialogMedia │ ├── AlertDialogTitle │ └── AlertDialogDescription └── AlertDialogFooter ├── AlertDialogCancel └── AlertDialogAction ``` ## 基本用法 带有标题、描述以及取消和继续按钮的基本警告对话框。 ### 示例: alert-dialog-basic ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; export function AlertDialogBasic() { return ( Show Dialog} /> Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ); } export default AlertDialogBasic; ``` ## 小尺寸 使用 `size="sm"` 属性使警告对话框更小。 ### 示例: alert-dialog-small ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; export function AlertDialogSmall() { return ( Show Dialog} /> Allow accessory to connect? Do you want to allow the USB accessory to connect to this device? Don't allow Allow ); } export default AlertDialogSmall; ``` ## 媒体 使用 `AlertDialogMedia` 组件将媒体元素(例如图标或图像)添加到警告对话框。 ### 示例: alert-dialog-media ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; import { CircleFadingPlusIcon } from "lucide-react"; export function AlertDialogWithMedia() { return ( Share Project} /> Share this project? Anyone with the link will be able to view and edit this project. Cancel Share ); } export default AlertDialogWithMedia; ``` ## 带媒体的小尺寸 使用 `size="sm"` 属性使警告对话框更小,并使用 `AlertDialogMedia` 组件将媒体元素(例如图标或图像)添加到警告对话框。 ### 示例: alert-dialog-small-media ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; import { BluetoothIcon } from "lucide-react"; export function AlertDialogSmallWithMedia() { return ( Show Dialog} /> Allow accessory to connect? Do you want to allow the USB accessory to connect to this device? Don't allow Allow ); } export default AlertDialogSmallWithMedia; ``` ## 危险操作 使用 `AlertDialogAction` 组件向警告对话框添加危险操作按钮。 ### 示例: alert-dialog-destructive ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; import { Trash2Icon } from "lucide-react"; export function AlertDialogDestructive() { return ( Delete Chat} /> Delete chat? This will permanently delete this chat conversation. View{" "} Settings delete any memories saved during this chat. Cancel Delete ); } export default AlertDialogDestructive; ``` ## 从右到左 关于 shadcn/ui 的 RTL 支持,参阅 [RTL 配置指南](https://ui.shadcn.com/docs/rtl)。 ### 示例: alert-dialog-rtl ```tsx "use client"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Button } from "@workspace/ui/components/button"; import { BluetoothIcon } from "lucide-react"; import { type Translations, useTranslation } from "./support"; const translations: Translations = { en: { dir: "ltr", values: { showDialog: "Show Dialog", showDialogSm: "Show Dialog (sm)", title: "Are you absolutely sure?", description: "This action cannot be undone. This will permanently delete your account from our servers.", cancel: "Cancel", continue: "Continue", smallTitle: "Allow accessory to connect?", smallDescription: "Do you want to allow the USB accessory to connect to this device?", dontAllow: "Don't allow", allow: "Allow", }, }, ar: { dir: "rtl", values: { showDialog: "إظهار الحوار", showDialogSm: "إظهار الحوار (صغير)", title: "هل أنت متأكد تمامًا؟", description: "لا يمكن التراجع عن هذا الإجراء. سيؤدي هذا إلى حذف حسابك نهائيًا من خوادمنا.", cancel: "إلغاء", continue: "متابعة", smallTitle: "السماح للملحق بالاتصال؟", smallDescription: "هل تريد السماح لملحق USB بالاتصال بهذا الجهاز؟", dontAllow: "عدم السماح", allow: "السماح", }, }, he: { dir: "rtl", values: { showDialog: "הצג דיאלוג", showDialogSm: "הצג דיאלוג (קטן)", title: "האם אתה בטוח לחלוטין?", description: "פעולה זו לא ניתנת לביטול. זה ימחק לצמיתות את החשבון שלך מהשרתים שלנו.", cancel: "ביטול", continue: "המשך", smallTitle: "לאפשר להתקן להתחבר?", smallDescription: "האם אתה רוצה לאפשר להתקן USB להתחבר למכשיר זה?", dontAllow: "אל תאפשר", allow: "אפשר", }, }, }; export function AlertDialogRtl() { const { dir, t, language } = useTranslation(translations, "ar"); return (
}> {t.showDialog} {t.title} {t.description} {t.cancel} {t.continue} }> {t.showDialogSm} {t.smallTitle} {t.smallDescription} {t.dontAllow} {t.allow}
); } export default AlertDialogRtl; ``` ## API 参考 ### size 使用 `AlertDialogContent` 组件上的 `size` 属性来控制警告对话框的大小。它接受以下值: | 属性 |类型 |默认 | | ------ | ------------------- | ----------- | | `size` | `"default" \| "sm"` | `"default"` | 有关其他组件及其 props 的更多信息,请参阅 [Base UI 文档](https://base-ui.com/react/components/alert-dialog#api-reference). - [Documentation](https://base-ui.com/react/components/alert-dialog) - [API reference](https://base-ui.com/react/components/alert-dialog#api-reference) --- # Aspect Ratio 以所需的比例显示内容 页面: https://sui.draco.dev/zh-CN/docs/components/aspect-ratio ### 示例: aspect-ratio-demo ```tsx import { AspectRatio } from "@workspace/ui/components/aspect-ratio"; export default function AspectRatioDemo() { return ( Landscape ); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/aspect-ratio ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx showLineNumbers import { AspectRatio } from "@workspace/ui/components/aspect-ratio" ``` ```tsx showLineNumbers Image ``` ## 正方形 使用 `ratio={1 / 1}` 属性的方形纵横比组件。这对于以方形格式显示图像很有用。 ### 示例: aspect-ratio-square ```tsx import { AspectRatio } from "@workspace/ui/components/aspect-ratio"; export function AspectRatioSquare() { return ( Landscape ); } export default AspectRatioSquare; ``` ## 竖向比例 使用 `ratio={9 / 16}` 属性的纵向纵横比组件。这对于以纵向格式显示图像非常有用。 ### 示例: aspect-ratio-portrait ```tsx import { AspectRatio } from "@workspace/ui/components/aspect-ratio"; export function AspectRatioPortrait() { return ( Landscape ); } export default AspectRatioPortrait; ``` ## 从右到左 关于 shadcn/ui 的 RTL 支持,参阅 [RTL 配置指南](https://ui.shadcn.com/docs/rtl)。 ### 示例: aspect-ratio-rtl ```tsx "use client"; import { AspectRatio } from "@workspace/ui/components/aspect-ratio"; import { type Translations, useTranslation } from "./support"; const translations: Translations = { en: { dir: "ltr", values: { caption: "Beautiful landscape", }, }, ar: { dir: "rtl", values: { caption: "منظر طبيعي جميل", }, }, he: { dir: "rtl", values: { caption: "נוף יפה", }, }, }; export function AspectRatioRtl() { const { dir, t } = useTranslation(translations, "ar"); return (
Landscape
{t.caption}
); } export default AspectRatioRtl; ``` ## API 参考 ### AspectRatio `AspectRatio` 组件以所需的比例显示内容 | 属性 |类型 |默认 |必填| | ----------- | -------- | -------- | -------- | | `ratio` | `number` | - |是的 | | `className` | `string` | - |没有 | 有关更多信息,请参阅[Base UI 文档](https://base-ui.com/react/components/aspect-ratio#api-reference)。 --- # Attachment 显示带有媒体、元数据、上传状态和操作的文件或图像附件 页面: https://sui.draco.dev/zh-CN/docs/components/attachment ### 示例: attachment-demo ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, } from "@workspace/ui/components/attachment"; import { Loader } from "@workspace/ui/components/loader"; import { FileCodeIcon, XIcon } from "lucide-react"; const images = [ { name: "workspace.png", meta: "PNG · 820 KB", src: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80", alt: "Workspace", }, { name: "desk-reference.jpg", meta: "JPG · 1.1 MB", src: "https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=900&auto=format&fit=crop&q=80", alt: "Desk", }, { name: "office-reference.jpg", meta: "JPG · 940 KB", src: "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=900&auto=format&fit=crop&q=80", alt: "Office", }, ]; export function AttachmentDemo() { return (
{images.map((image) => ( {image.alt} {image.name} {image.meta} ))} sales-dashboard.pdf Uploading · 64% message-renderer.tsx TypeScript · 12 KB
); } export default AttachmentDemo; ``` `Attachment` 显示文件或图片附件的预览、名称与元数据,也可包含操作按钮及上传状态。适用于聊天输入区、消息对话与上传列表。 ## 安装 ```bash bunx --bun shadcn@latest add @sui/attachment ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, } from "@workspace/ui/components/attachment" ``` ```tsx sales-dashboard.pdf PDF · 2.4 MB ``` ## 组合结构 使用以下组合来构建附件: ```text Attachment ├── AttachmentMedia ├── AttachmentContent │ ├── AttachmentTitle │ └── AttachmentDescription ├── AttachmentActions │ └── AttachmentAction └── AttachmentTrigger ``` 使用 `AttachmentGroup` 将多个附件布置在可滚动行中: ```text AttachmentGroup ├── Attachment └── Attachment ``` ## 特性 - 通过 `AttachmentMedia` 显示图标或图片 - 上传状态 `idle`、`uploading`、`processing`、`error` 与 `done` 均有内置样式,进行中的状态显示流光效果 - 三种尺寸,可选择水平或垂直布局 - `AttachmentTrigger` 覆盖整个卡片,用于打开链接或对话框;其他操作仍可独立点击 - `AttachmentGroup` 支持水平滚动、滚动吸附与边缘渐隐 - 每个子组件都可通过 `className` 自定义样式 ## 图片 在`AttachmentMedia`上设置`variant="image"`并在其中渲染一个``。使用 `orientation="vertical"` 将媒体堆叠在内容上方。 ### 示例: attachment-image ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger, } from "@workspace/ui/components/attachment"; import { XIcon } from "lucide-react"; const images = [ { name: "workspace.png", meta: "PNG · 820 KB", src: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80", alt: "Workspace", }, { name: "desk-reference.jpg", meta: "JPG · 1.1 MB", src: "https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=900&auto=format&fit=crop&q=80", alt: "Desk", }, { name: "office-reference.jpg", meta: "JPG · 940 KB", src: "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=900&auto=format&fit=crop&q=80", alt: "Office", }, ]; export function AttachmentImage() { return (
{images.map((image) => ( {image.alt} {image.name} {image.meta} } /> ))}
); } export default AttachmentImage; ``` ## 状态 使用 `state` 表达上传过程。`uploading` 与 `processing` 会让标题显示流光效果,`error` 则应用危险状态样式。 ### 示例: attachment-states ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, } from "@workspace/ui/components/attachment"; import { Loader } from "@workspace/ui/components/loader"; import { CheckIcon, ClockIcon, FileTextIcon, FileWarningIcon, RefreshCwIcon, XIcon, } from "lucide-react"; export function AttachmentStates() { return (
selected-file.pdf Ready to upload design-system.zip Uploading · 64% market-research.pdf Processing document financial-model.xlsx Upload failed. Try again. uploaded-report.pdf Uploaded · 1.8 MB
); } export default AttachmentStates; ``` ## 尺寸 使用`size`在`default`、`sm`和`xs`之间切换。 ### 示例: attachment-sizes ```tsx import { Attachment, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, } from "@workspace/ui/components/attachment"; import { FileTextIcon } from "lucide-react"; export function AttachmentSizes() { return (
Default attachment PDF · 2.4 MB Small attachment PDF · 2.4 MB Extra small attachment
); } export default AttachmentSizes; ``` ## 组合 使用 `AttachmentGroup` 包裹多个附件,将其排列在支持滚动吸附与边缘渐隐的水平滚动区域中。 ### 示例: attachment-group ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, } from "@workspace/ui/components/attachment"; import { FileCodeIcon, FileTextIcon, type LucideIcon, TableIcon, XIcon, } from "lucide-react"; import type { ReactNode } from "react"; type Item = { name: string; meta: string; icon?: LucideIcon; src?: string; }; const items: Item[] = [ { name: "briefing-notes.pdf", meta: "PDF · 1.4 MB", icon: FileTextIcon }, { name: "workspace.png", meta: "PNG · 820 KB", src: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80", }, { name: "customers.csv", meta: "CSV · 18 KB", icon: TableIcon }, { name: "renderer.tsx", meta: "TSX · 12 KB", icon: FileCodeIcon }, ]; export function AttachmentGroupDemo() { return (
{items.map((item) => { const Icon = item.icon; let media: ReactNode = null; if (item.src) { media = ( {item.name} ); } else if (Icon) { media = ( ); } return ( {media} {item.name} {item.meta} ); })}
); } export default AttachmentGroupDemo; ``` ## 触发器 添加 `AttachmentTrigger`,让整张卡片都可打开链接或对话框。触发器覆盖卡片,但位于操作按钮后方,因此其他操作仍可点击。 ### 示例: attachment-trigger ```tsx import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, AttachmentTrigger, } from "@workspace/ui/components/attachment"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@workspace/ui/components/dialog"; import { CopyIcon, FileSearchIcon, XIcon } from "lucide-react"; export function AttachmentTriggerDemo() { return (
research-summary.pdf Open preview dialog } /> research-summary.pdf The attachment trigger fills the card and opens the dialog, while the actions stay independently clickable above it.
); } export default AttachmentTriggerDemo; ``` ```tsx showLineNumbers {/* media, content, actions */} } /> {/* ... */} ``` ## 无障碍 `AttachmentAction` 渲染为 Button,`AttachmentTrigger` 默认渲染为真实的 ` ); } export default BubbleReactionsDemo; ``` ## 显示更多与折叠 长内容可与 [`Collapsible`](/zh-CN/docs/components/collapsible) 组合,提供“显示更多”和“收起”操作。使用 `CollapsibleTrigger` 触发展开与折叠。 ### 示例: bubble-collapsible ```tsx "use client"; import { Bubble, BubbleContent } from "@workspace/ui/components/bubble"; import { Button } from "@workspace/ui/components/button"; import { Collapsible, CollapsibleTrigger, } from "@workspace/ui/components/collapsible"; import { ChevronDownIcon } from "lucide-react"; import * as React from "react"; const text = `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 controls inside a layered surface. The dialog and drawer are fine. The menu needs the hover and focus tokens split so keyboard focus stays visible when the pointer is not involved. I also recommend keeping the change in the style file instead of the primitive so the other themes can choose their own focus treatment later.`; const previewLength = 180; export function BubbleCollapsible() { const [open, setOpen] = React.useState(false); const isLong = text.length > previewLength; const preview = `${text.slice(0, previewLength)}...`; return (
How can I help you today?
{open || !isLong ? text : preview}
{isLong ? ( } > {open ? "Show less" : "Show more"} ) : null}
); } export default BubbleCollapsible; ``` ## 提示框组合 用 [`Tooltip`](/zh-CN/docs/components/tooltip) 包裹气泡,即可在悬停时显示元数据,例如消息的已读时间。 ### 示例: bubble-tooltip ```tsx import { Bubble, BubbleContent, BubbleReactions, } from "@workspace/ui/components/bubble"; import { Button } from "@workspace/ui/components/button"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@workspace/ui/components/tooltip"; import { CheckIcon } from "lucide-react"; export function BubbleTooltipDemo() { return (
Did you remove the stale route? Yes, removed it from the registry. }> Read on Jan 5, 2026 at 4:32 PM
); } export default BubbleTooltipDemo; ``` ## 弹出层组合 将气泡与 [`Popover`](/zh-CN/docs/components/popover) 配对,可按需显示更多信息,例如失败操作的完整错误消息。 ### 示例: bubble-popover ```tsx import { Bubble, BubbleContent, BubbleReactions, } from "@workspace/ui/components/bubble"; import { Button } from "@workspace/ui/components/button"; import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from "@workspace/ui/components/popover"; import { InfoIcon } from "lucide-react"; export function BubblePopoverDemo() { return (
Run the build script. Failed to run the command. } > Command failed with exit code 1 ENOENT: no such file or directory, open pnpm-lock.yaml
); } export default BubblePopoverDemo; ``` ## 无障碍 `Bubble` 只提供消息气泡的展示样式。对话级语义应设置在外层容器上,并遵循以下要求。 ### 为回应提供标签 表情回应通常排列成一行。缺少上下文时,屏幕阅读器会逐个朗读表情,而 `+8` 会被读成“加八”。使用 `role="img"` 和描述性的 `aria-label`,将整行表示为一个图像并一次播报。`role="img"` 也会向辅助技术隐藏内部表情,因此无需额外设置 `aria-hidden`。 ```tsx showLineNumbers 👍 🔥 +8 ``` 如果回应可以点击,应改用按钮,并为纯图标按钮提供 `aria-label`。 ```tsx showLineNumbers ``` ### 交互式气泡 气泡可以点击时,通过 `render` 将其渲染为真实的 ` ); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/button ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx import { Button } from "@workspace/ui/components/button" ``` ```tsx ``` ## 鼠标指针 Tailwind v4 将按钮的默认鼠标指针[从 `cursor: pointer` 改为 `cursor: default`](https://tailwindcss.com/docs/upgrade-guide#buttons-use-the-default-cursor)。 如果您想保留 `cursor: pointer` 行为,请将以下代码添加到 CSS 文件中: 也可在配置 shadcn/ui 项目时使用 `npx shadcn@latest init --pointer` 启用该行为。 ```css showLineNumbers title="globals.css" @layer base { button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer; } } ``` ## 尺寸 使用 `size` 属性更改按钮的大小。 ### 示例: button-size ```tsx import { Button } from "@workspace/ui/components/button"; import { ArrowUpRightIcon } from "lucide-react"; export default function ButtonSize() { return (
); } ``` ## 默认样式 ### 示例: button-default ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonDefault() { return ; } ``` ## 描边 ### 示例: button-outline ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonOutline() { return ; } ``` ## 次级样式 ### 示例: button-secondary ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonSecondary() { return ; } ``` ## 透明 ### 示例: button-ghost ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonGhost() { return ; } ``` ## 危险操作 ### 示例: button-destructive ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonDestructive() { return ; } ``` ## 链接 ### 示例: button-link ```tsx import { Button } from "@workspace/ui/components/button"; export default function ButtonLink() { return ; } ``` ## 图标 ### 示例: button-icon ```tsx import { Button } from "@workspace/ui/components/button"; import { CircleFadingArrowUpIcon } from "lucide-react"; export default function ButtonIcon() { return ( ); } ``` ## 带图标 请记住将 `data-icon="inline-start"` 或 `data-icon="inline-end"` 属性添加到图标以获得正确的间距。 ### 示例: button-with-icon ```tsx import { Button } from "@workspace/ui/components/button"; import { GitBranchIcon, GitForkIcon } from "lucide-react"; export default function ButtonWithIcon() { return (
); } ``` ## 圆角 使用 `rounded-full` 类使按钮变圆。 ### 示例: button-rounded ```tsx import { Button } from "@workspace/ui/components/button"; import { ArrowUpIcon } from "lucide-react"; export default function ButtonRounded() { return (
); } ``` ## 加载状态 在按钮内渲染 `` 组件以显示加载状态。请记住将 `data-icon="inline-start"` 或 `data-icon="inline-end"` 属性添加到Loader以获得正确的间距。 ### 示例: button-loading ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { Loader } from "@workspace/ui/components/loader"; import { useEffect, useRef, useState } from "react"; import type { ExampleProps } from "../types"; export default function ButtonLoading({ locale }: ExampleProps) { const zh = locale === "zh-CN"; const stateLabels = zh ? { generating: "正在生成", generate: "生成", downloading: "正在下载", download: "下载", done: "演示完成,可以再次点击", idle: "点击按钮模拟加载状态", } : { generating: "Generating", generate: "Generate", downloading: "Downloading", download: "Download", done: "Demo complete. Try it again.", idle: "Click a button to simulate loading.", }; const [status, setStatus] = useState<"idle" | "pending" | "done">("idle"); const timer = useRef | undefined>(undefined); const pending = status === "pending"; useEffect(() => () => clearTimeout(timer.current), []); function startDemo() { clearTimeout(timer.current); setStatus("pending"); // Replace this delay with your application's async operation. timer.current = setTimeout(() => setStatus("done"), 1200); } return (

{status === "done" ? stateLabels.done : stateLabels.idle}

); } ``` ## 按钮组 要创建按钮组,请使用 `ButtonGroup` 组件。有关更多详细信息,请参阅[按钮组](/zh-CN/docs/components/button-group) 文档。 ### 示例: button-group-demo ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@workspace/ui/components/dropdown-menu"; import { ArchiveIcon, ArrowLeftIcon, CalendarPlusIcon, ClockIcon, ListFilterIcon, MailCheckIcon, MoreHorizontalIcon, TagIcon, Trash2Icon, } from "lucide-react"; import * as React from "react"; export default function ButtonGroupDemo() { const [label, setLabel] = React.useState("personal"); return ( } > Mark as Read Archive Snooze Add to Calendar Add to List Label As... Personal Work Other Trash ); } ``` ## 作为链接 使用 `buttonVariants` 辅助函数为链接应用按钮样式。 **请勿将 `
); } export default ButtonRtl; ``` ## API 参考 ### Button `Button` 组件是 `button` 元素的包装器,添加了各种样式和功能 | 属性 |类型 |默认 | | --------- | ------------------------------------------------------------------------------------------------ | ----------- | | `variant` | `"default" \| "outline" \| "ghost" \| "destructive" \| "secondary" \| "link"` | `"default"` | | `size` | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg"` | `"default"` | 底层交互行为与底层属性请参阅 [Base UI API](https://base-ui.com/react/components/button#api-reference)。 --- # Button Group 将相关按钮分组在一起并具有一致样式的容器 页面: https://sui.draco.dev/zh-CN/docs/components/button-group ### 示例: button-group-demo ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@workspace/ui/components/dropdown-menu"; import { ArchiveIcon, ArrowLeftIcon, CalendarPlusIcon, ClockIcon, ListFilterIcon, MailCheckIcon, MoreHorizontalIcon, TagIcon, Trash2Icon, } from "lucide-react"; import * as React from "react"; export default function ButtonGroupDemo() { const [label, setLabel] = React.useState("personal"); return ( } > Mark as Read Archive Snooze Add to Calendar Add to List Label As... Personal Work Other Trash ); } ``` ## 安装 ```bash bunx --bun shadcn@latest add @sui/button-group ``` 通过 shadcn CLI 安装组件源码。按照[安装指南](/zh-CN/docs/installation)配置 registry、加载样式并选择导入别名。 ## 使用方法 ```tsx import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, } from "@workspace/ui/components/button-group" ``` ```tsx ``` ## 组合结构 使用以下组合来构建 `ButtonGroup`: ```text ButtonGroup ├── Button or Input ├── ButtonGroupSeparator └── ButtonGroupText ``` ## 无障碍 - `ButtonGroup` 组件的 `role` 属性设置为 `group`。 - 使用 Tab 在组中的按钮之间导航。 - 使用`aria-label`或`aria-labelledby`来标记按钮组。 ```tsx showLineNumbers ``` ## ButtonGroup 与 ToggleGroup - 当您想要对执行操作的按钮进行分组时,请使用 `ButtonGroup` 组件。 - 当您想要对切换状态的按钮进行分组时,请使用 `ToggleGroup` 组件。 ## 方向 设置 `orientation` 属性以更改按钮组布局。 ### 示例: button-group-orientation ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { MinusIcon, PlusIcon } from "lucide-react"; export default function ButtonGroupOrientation() { return ( ); } ``` ## 尺寸 使用各个按钮上的 `size` 属性控制按钮的大小。 ### 示例: button-group-size ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { PlusIcon } from "lucide-react"; export default function ButtonGroupSize() { return (
); } ``` ## 嵌套 嵌套 `` 组件以创建带间距的按钮组。 ### 示例: button-group-nested ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { InputGroup, InputGroupAddon, InputGroupInput, } from "@workspace/ui/components/input-group"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@workspace/ui/components/tooltip"; import { AudioLinesIcon, PlusIcon } from "lucide-react"; export function ButtonGroupNested() { return ( }> Voice Mode ); } export default ButtonGroupNested; ``` ## 分隔线 `ButtonGroupSeparator` 组件在视觉上将按钮划分为一组。 具有变体 `outline` 的按钮不需要分隔符,因为它们有边框。对于其他变体,建议使用分隔符来改善视觉层次结构。 ### 示例: button-group-separator ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup, ButtonGroupSeparator, } from "@workspace/ui/components/button-group"; export default function ButtonGroupSeparatorDemo() { return ( ); } ``` ## 拆分按钮 通过添加由 `ButtonGroupSeparator` 分隔的两个按钮来创建拆分按钮组。 ### 示例: button-group-split ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup, ButtonGroupSeparator, } from "@workspace/ui/components/button-group"; import { PlusIcon } from "lucide-react"; export default function ButtonGroupSplit() { return ( ); } ``` ## 输入框组合 用按钮包裹 `Input` 组件。 ### 示例: button-group-input ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { Input } from "@workspace/ui/components/input"; import { SearchIcon } from "lucide-react"; export default function ButtonGroupInput() { return ( ); } ``` ## 输入框组 封装 `InputGroup` 组件以创建复杂的输入布局。 ### 示例: button-group-input-group ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, } from "@workspace/ui/components/input-group"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@workspace/ui/components/tooltip"; import { AudioLinesIcon, PlusIcon } from "lucide-react"; import * as React from "react"; export default function ButtonGroupInputGroup() { const [voiceEnabled, setVoiceEnabled] = React.useState(false); return ( setVoiceEnabled(!voiceEnabled)} size="icon-xs" data-active={voiceEnabled} className="data-[active=true]:bg-orange-100 data-[active=true]:text-orange-700 dark:data-[active=true]:bg-orange-800 dark:data-[active=true]:text-orange-100" aria-pressed={voiceEnabled} /> } > Voice Mode ); } ``` ## 下拉菜单组合 使用 `DropdownMenu` 组件创建拆分按钮组。 ### 示例: button-group-dropdown ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@workspace/ui/components/dropdown-menu"; import { AlertTriangleIcon, CheckIcon, ChevronDownIcon, CopyIcon, ShareIcon, TrashIcon, UserRoundXIcon, VolumeOffIcon, } from "lucide-react"; export default function ButtonGroupDropdown() { return ( } > Mute Conversation Mark as Read Report Conversation Block User Share Conversation Copy Conversation Delete Conversation ); } ``` ## 选择器组合 与 `Select` 组件配对。 ### 示例: button-group-select ```tsx "use client"; import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { Input } from "@workspace/ui/components/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, } from "@workspace/ui/components/select"; import { ArrowRightIcon } from "lucide-react"; import * as React from "react"; const CURRENCIES = [ { label: "US Dollar", value: "$" }, { label: "Euro", value: "€" }, { label: "British Pound", value: "£" }, ]; export default function ButtonGroupSelect() { const [currency, setCurrency] = React.useState("$"); return ( ); } ``` ## 弹出层组合 与 `Popover` 组件一起使用。 ### 示例: button-group-popover ```tsx import { Button } from "@workspace/ui/components/button"; import { ButtonGroup } from "@workspace/ui/components/button-group"; import { Field, FieldDescription, FieldLabel, } from "@workspace/ui/components/field"; import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from "@workspace/ui/components/popover"; import { Textarea } from "@workspace/ui/components/textarea"; import { BotIcon, ChevronDownIcon } from "lucide-react"; import { useId as usePreviewId } from "react"; export default function ButtonGroupPopover() { const previewId = usePreviewId(); return ( } > Start a new task with Copilot Describe your task in natural language. Task Description