chore: import zh skill shadcn
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- Skill 名称:`shadcn`
|
||||
- 中文类目:shadcn/ui 组件初始化与定制
|
||||
- 上游仓库:`sickn33__antigravity-awesome-skills`
|
||||
- 上游路径:`plugins/antigravity-awesome-skills-claude/skills/shadcn/SKILL.md`
|
||||
- 上游链接:https://github.com/sickn33/antigravity-awesome-skills/blob/HEAD/plugins/antigravity-awesome-skills-claude/skills/shadcn/SKILL.md
|
||||
- 本仓库为 WeHub 中文 Skill 汉化包,基于 skill 市场筛选 Top200 清单整理
|
||||
- 原作者、版权和许可证信息以上游仓库为准
|
||||
@@ -0,0 +1,254 @@
|
||||
---
|
||||
name: shadcn
|
||||
description: 管理 shadcn/ui 组件和项目,提供上下文、文档和使用模式,用于构建现代设计系统。
|
||||
user-invocable: false
|
||||
risk: safe
|
||||
source: https://github.com/shadcn-ui/ui/tree/main/skills/shadcn
|
||||
date_added: "2026-03-07"
|
||||
---
|
||||
|
||||
# shadcn/ui
|
||||
|
||||
一个用于构建 UI、组件和设计系统的框架。组件通过 CLI 以源代码形式添加到用户项目中。
|
||||
|
||||
> **重要提示:** 使用项目的包运行器执行所有 CLI 命令:`npx shadcn@latest`、`pnpm dlx shadcn@latest` 或 `bunx --bun shadcn@latest`——基于项目的 `packageManager`。以下示例使用 `npx shadcn@latest`,但请替换为项目对应的正确运行器。
|
||||
|
||||
## 何时使用
|
||||
- 当从 shadcn/ui 或社区注册表添加新组件时使用。
|
||||
- 当对现有 shadcn/ui 组件进行样式设置、组合或调试时使用。
|
||||
- 当初始化新项目或切换设计系统预设时使用。
|
||||
- 用于获取组件文档、示例和 API 参考。
|
||||
|
||||
## 当前项目上下文
|
||||
|
||||
```json
|
||||
!`npx shadcn@latest info --json 2>/dev/null || echo '{"error": "No shadcn project found. Run shadcn init first."}'`
|
||||
```
|
||||
|
||||
上述 JSON 包含项目配置和已安装的组件。使用 `npx shadcn@latest docs <component>` 获取任意组件的文档和示例 URL。
|
||||
|
||||
## 原则
|
||||
|
||||
1. **优先使用现有组件。** 在编写自定义 UI 之前,使用 `npx shadcn@latest search` 检查注册表。同时也要检查社区注册表。
|
||||
2. **组合,不要重新发明。** 设置页面 = Tabs + Card + 表单控件。仪表板 = Sidebar + Card + Chart + Table。
|
||||
3. **优先使用内置变体,而非自定义样式。** 例如 `variant="outline"`、`size="sm"` 等。
|
||||
4. **使用语义化颜色。** 例如 `bg-primary`、`text-muted-foreground`——绝不使用 `bg-blue-500` 这样的原始值。
|
||||
|
||||
## 关键规则
|
||||
|
||||
以下规则**始终强制执行**。每条规则链接到一个包含错误/正确代码对的文件。
|
||||
|
||||
### 样式与 Tailwind → [styling.md](./rules/styling.md)
|
||||
|
||||
- **`className` 用于布局,而非样式。** 切勿覆盖组件的颜色或排版。
|
||||
- **不要使用 `space-x-*` 或 `space-y-*`。** 使用 `flex` 配合 `gap-*`。对于垂直堆叠,使用 `flex flex-col gap-*`。
|
||||
- **当宽度和高度相等时使用 `size-*`。** 使用 `size-10` 而非 `w-10 h-10`。
|
||||
- **使用 `truncate` 简写。** 而非 `overflow-hidden text-ellipsis whitespace-nowrap`。
|
||||
- **不要手动使用 `dark:` 颜色覆盖。** 使用语义化令牌(`bg-background`、`text-muted-foreground`)。
|
||||
- **使用 `cn()` 处理条件类名。** 不要手写模板字面量三元表达式。
|
||||
- **覆盖类组件上不要手动设置 `z-index`。** Dialog、Sheet、Popover 等会自行处理层级。
|
||||
|
||||
### 表单与输入 → [forms.md](./rules/forms.md)
|
||||
|
||||
- **表单使用 `FieldGroup` + `Field`。** 切勿在表单布局中使用原始的 `div` 配合 `space-y-*` 或 `grid gap-*`。
|
||||
- **`InputGroup` 使用 `InputGroupInput`/`InputGroupTextarea`。** 切勿在 `InputGroup` 内部使用原始的 `Input`/`Textarea`。
|
||||
- **输入框内的按钮使用 `InputGroup` + `InputGroupAddon`。**
|
||||
- **选项集(2–7 个选项)使用 `ToggleGroup`。** 不要循环使用 `Button` 并手动维护激活状态。
|
||||
- **使用 `FieldSet` + `FieldLegend` 对相关复选框/单选框进行分组。** 不要使用带标题的 `div`。
|
||||
- **字段验证使用 `data-invalid` + `aria-invalid`。** 在 `Field` 上使用 `data-invalid`,在控件上使用 `aria-invalid`。对于禁用状态:在 `Field` 上使用 `data-disabled`,在控件上使用 `disabled`。
|
||||
|
||||
### 组件结构 → [composition.md](./rules/composition.md)
|
||||
|
||||
- **项始终放在其 Group 内部。** `SelectItem` → `SelectGroup`。`DropdownMenuItem` → `DropdownMenuGroup`。`CommandItem` → `CommandGroup`。
|
||||
- **使用 `asChild`(radix)或 `render`(base)实现自定义触发器。** 从 `npx shadcn@latest info` 检查 `base` 字段。→ [base-vs-radix.md](./rules/base-vs-radix.md)
|
||||
- **Dialog、Sheet 和 Drawer 始终需要 Title。** `DialogTitle`、`SheetTitle`、`DrawerTitle` 对无障碍访问是必需的。如果视觉上需要隐藏,使用 `className="sr-only"`。
|
||||
- **使用完整的 Card 组合。** `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`。不要把所有内容都塞进 `CardContent`。
|
||||
- **Button 没有 `isPending`/`isLoading`。** 使用 `Spinner` + `data-icon` + `disabled` 组合。
|
||||
- **`TabsTrigger` 必须放在 `TabsList` 内部。** 切勿直接在 `Tabs` 中渲染触发器。
|
||||
- **`Avatar` 始终需要 `AvatarFallback`。** 用于图片加载失败的情况。
|
||||
|
||||
### 使用组件,而非自定义标记 → [composition.md](./rules/composition.md)
|
||||
|
||||
- **在编写自定义标记之前,优先使用现有组件。** 在编写带样式的 `div` 之前,先检查是否存在可用的组件。
|
||||
- **提示/标注使用 `Alert`。** 不要构建自定义样式的 div。
|
||||
- **空状态使用 `Empty`。** 不要构建自定义的空状态标记。
|
||||
- **Toast 通过 `sonner` 实现。** 使用 `sonner` 中的 `toast()` 函数。
|
||||
- **使用 `Separator`** 替代 `<hr>` 或 `<div className="border-t">`。
|
||||
- **使用 `Skeleton`** 作为加载占位符。不要使用自定义的 `animate-pulse` div。
|
||||
- **使用 `Badge`** 替代自定义样式的 span。
|
||||
|
||||
### 图标 → [icons.md](./rules/icons.md)
|
||||
|
||||
- **`Button` 中的图标使用 `data-icon`。** 在图标上使用 `data-icon="inline-start"` 或 `data-icon="inline-end"`。
|
||||
- **组件内部的图标不要添加尺寸类。** 组件通过 CSS 处理图标尺寸。不要使用 `size-4` 或 `w-4 h-4`。
|
||||
- **将图标作为对象传递,而非字符串键。** 使用 `icon={CheckIcon}`,而不是字符串查找。
|
||||
|
||||
### CLI
|
||||
|
||||
- **切勿手动解码或获取预设代码。** 直接将其传递给 `npx shadcn@latest init --preset <code>`。
|
||||
|
||||
## 关键模式
|
||||
|
||||
以下是区分正确 shadcn/ui 代码的最常见模式。对于边界情况,请参见上面链接的规则文件。
|
||||
|
||||
```tsx
|
||||
// 表单布局:使用 FieldGroup + Field,而非 div + Label。
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">邮箱</FieldLabel>
|
||||
<Input id="email" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
|
||||
// 验证:在 Field 上使用 data-invalid,在控件上使用 aria-invalid。
|
||||
<Field data-invalid>
|
||||
<FieldLabel>邮箱</FieldLabel>
|
||||
<Input aria-invalid />
|
||||
<FieldDescription>无效的邮箱地址。</FieldDescription>
|
||||
</Field>
|
||||
|
||||
// 按钮中的图标:使用 data-icon,不要添加尺寸类。
|
||||
<Button>
|
||||
<SearchIcon data-icon="inline-start" />
|
||||
搜索
|
||||
</Button>
|
||||
|
||||
// 间距:使用 gap-*,而非 space-y-*。
|
||||
<div className="flex flex-col gap-4"> // 正确
|
||||
<div className="space-y-4"> // 错误
|
||||
|
||||
// 相等尺寸:使用 size-*,而非 w-* h-*。
|
||||
<Avatar className="size-10"> // 正确
|
||||
<Avatar className="w-10 h-10"> // 错误
|
||||
|
||||
// 状态颜色:使用 Badge 变体或语义化令牌,而非原始颜色。
|
||||
<Badge variant="secondary">+20.1%</Badge> // 正确
|
||||
<span className="text-emerald-600">+20.1%</span> // 错误
|
||||
```
|
||||
|
||||
## 组件选择
|
||||
|
||||
| 需求 | 使用 |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------------- |
|
||||
| 按钮/操作 | `Button`,配合适当的 variant |
|
||||
| 表单输入 | `Input`、`Select`、`Combobox`、`Switch`、`Checkbox`、`RadioGroup`、`Textarea`、`InputOTP`、`Slider` |
|
||||
| 在 2–5 个选项之间切换 | `ToggleGroup` + `ToggleGroupItem` |
|
||||
| 数据展示 | `Table`、`Card`、`Badge`、`Avatar` |
|
||||
| 导航 | `Sidebar`、`NavigationMenu`、`Breadcrumb`、`Tabs`、`Pagination` |
|
||||
| 覆盖层 | `Dialog`(模态框)、`Sheet`(侧面板)、`Drawer`(底部面板)、`AlertDialog`(确认弹窗) |
|
||||
| 反馈 | `sonner`(Toast)、`Alert`、`Progress`、`Skeleton`、`Spinner` |
|
||||
| 命令面板 | `Dialog` 内部的 `Command` |
|
||||
| 图表 | `Chart`(封装 Recharts) |
|
||||
| 布局 | `Card`、`Separator`、`Resizable`、`ScrollArea`、`Accordion`、`Collapsible` |
|
||||
| 空状态 | `Empty` |
|
||||
| 菜单 | `DropdownMenu`、`ContextMenu`、`Menubar` |
|
||||
| 工具提示/信息 | `Tooltip`、`HoverCard`、`Popover` |
|
||||
|
||||
## 关键字段
|
||||
|
||||
注入的项目上下文包含以下关键字段:
|
||||
|
||||
- **`aliases`** → 为导入使用实际的别名前缀(例如 `@/`、`~/`),切勿硬编码。
|
||||
- **`isRSC`** → 当为 `true` 时,使用 `useState`、`useEffect`、事件处理器或浏览器 API 的组件需要在文件顶部添加 `"use client"`。在就此指令提供建议时,始终参考此字段。
|
||||
- **`tailwindVersion`** → `"v4"` 使用 `@theme inline` 块;`"v3"` 使用 `tailwind.config.js`。
|
||||
- **`tailwindCssFile`** → 定义自定义 CSS 变量的全局 CSS 文件。始终编辑此文件,切勿新建。
|
||||
- **`style`** → 组件视觉风格(例如 `nova`、`vega`)。
|
||||
- **`base`** → 底层基础库(`radix` 或 `base`)。影响组件 API 和可用属性。
|
||||
- **`iconLibrary`** → 决定图标的导入来源。`lucide` 使用 `lucide-react`,`tabler` 使用 `@tabler/icons-react`,以此类推。切勿假设使用 `lucide-react`。
|
||||
- **`resolvedPaths`** → 组件、工具函数、Hook 等的精确文件系统目标路径。
|
||||
- **`framework`** → 路由和文件约定(例如 Next.js App Router 与 Vite SPA)。
|
||||
- **`packageManager`** → 用于安装任何非 shadcn 依赖(例如 `pnpm add date-fns` 与 `npm install date-fns`)。
|
||||
|
||||
完整字段参考请参见 [cli.md — `info` 命令](./cli.md)。
|
||||
|
||||
## 组件文档、示例与用法
|
||||
|
||||
运行 `npx shadcn@latest docs <component>` 获取组件的文档、示例和 API 参考的 URL。获取这些 URL 以查看实际内容。
|
||||
|
||||
```bash
|
||||
npx shadcn@latest docs button dialog select
|
||||
```
|
||||
|
||||
**在创建、修复、调试或使用组件时,始终先运行 `npx shadcn@latest docs` 并获取 URL。** 这确保你使用的是正确的 API 和使用模式,而非凭空猜测。
|
||||
|
||||
## 工作流程
|
||||
|
||||
1. **获取项目上下文**——已在上方注入。如果需要刷新,再次运行 `npx shadcn@latest info`。
|
||||
2. **首先检查已安装的组件**——在运行 `add` 之前,始终检查项目上下文中的 `components` 列表,或列出 `resolvedPaths.ui` 目录。不要导入尚未添加的组件,也不要重复添加已安装的组件。
|
||||
3. **查找组件**——`npx shadcn@latest search`。
|
||||
4. **获取文档和示例**——运行 `npx shadcn@latest docs <component>` 获取 URL,然后获取这些 URL 的内容。使用 `npx shadcn@latest view` 浏览尚未安装的注册表项。要预览对已安装组件的更改,使用 `npx shadcn@latest add --diff`。
|
||||
5. **安装或更新**——`npx shadcn@latest add`。更新现有组件时,先使用 `--dry-run` 和 `--diff` 预览更改(请参见下文[更新组件](#updating-components))。
|
||||
6. **修复第三方组件中的导入路径**——从社区注册表(例如 `@bundui`、`@magicui`)添加组件后,检查新增的非 UI 文件中是否存在硬编码的导入路径(如 `@/components/ui/...`)。这些路径可能与项目实际的别名不匹配。使用 `npx shadcn@latest info` 获取正确的 `ui` 别名(例如 `@workspace/ui/components`)并相应重写导入。CLI 会为其自己的 UI 文件重写导入路径,但第三方注册表组件可能使用与项目不匹配的默认路径。
|
||||
7. **审查新增的组件**——从任何注册表添加组件或块之后,**始终读取新增的文件并验证其正确性**。检查是否缺少子组件(例如 `SelectItem` 没有 `SelectGroup`)、缺少导入、组合不正确或违反[关键规则](#关键规则)。同时将所有图标导入替换为项目上下文中的 `iconLibrary`(例如,如果注册表项使用 `lucide-react` 但项目使用 `hugeicons`,则相应替换导入和图标名称)。解决所有问题后再继续。
|
||||
8. **注册表必须明确指定**——当用户要求添加块或组件时,**不要猜测注册表**。如果没有指定注册表(例如用户说"添加一个登录块"但没有指定 `@shadcn`、`@tailark` 等),询问使用哪个注册表。切勿代表用户默认选择注册表。
|
||||
9. **切换预设**——先询问用户:**重新安装(reinstall)**、**合并(merge)** 还是 **跳过(skip)**?
|
||||
- **重新安装**:`npx shadcn@latest init --preset <code> --force --reinstall`。覆盖所有组件。
|
||||
- **合并**:`npx shadcn@latest init --preset <code> --force --no-reinstall`,然后运行 `npx shadcn@latest info` 列出已安装的组件,再对每个已安装组件分别使用 `--dry-run` 和 `--diff` 进行[智能合并](#updating-components)。
|
||||
- **跳过**:`npx shadcn@latest init --preset <code> --force --no-reinstall`。仅更新配置和 CSS,组件保持不变。
|
||||
|
||||
## 更新组件
|
||||
|
||||
当用户要求从上游更新组件同时保留本地更改时,使用 `--dry-run` 和 `--diff` 进行智能合并。**切勿手动从 GitHub 获取原始文件——始终使用 CLI。**
|
||||
|
||||
1. 运行 `npx shadcn@latest add <component> --dry-run` 查看所有会受到影响的文件。
|
||||
2. 对每个文件,运行 `npx shadcn@latest add <component> --diff <file>` 查看上游与本地之间的差异。
|
||||
3. 根据差异逐文件决定:
|
||||
- 无本地更改→可安全覆盖。
|
||||
- 有本地更改→读取本地文件,分析差异,在保留本地修改的同时应用上游更新。
|
||||
- 用户说"直接更新所有内容"→使用 `--overwrite`,但先确认。
|
||||
4. **未经用户明确批准,切勿使用 `--overwrite`。**
|
||||
|
||||
## 快速参考
|
||||
|
||||
```bash
|
||||
# 创建新项目。
|
||||
npx shadcn@latest init --name my-app --preset base-nova
|
||||
npx shadcn@latest init --name my-app --preset a2r6bw --template vite
|
||||
|
||||
# 创建单体仓库项目。
|
||||
npx shadcn@latest init --name my-app --preset base-nova --monorepo
|
||||
npx shadcn@latest init --name my-app --preset base-nova --template next --monorepo
|
||||
|
||||
# 初始化现有项目。
|
||||
npx shadcn@latest init --preset base-nova
|
||||
npx shadcn@latest init --defaults # 快捷方式:--template=next --preset=base-nova
|
||||
|
||||
# 添加组件。
|
||||
npx shadcn@latest add button card dialog
|
||||
npx shadcn@latest add @magicui/shimmer-button
|
||||
npx shadcn@latest add --all
|
||||
|
||||
# 在添加/更新之前预览更改。
|
||||
npx shadcn@latest add button --dry-run
|
||||
npx shadcn@latest add button --diff button.tsx
|
||||
npx shadcn@latest add @acme/form --view button.tsx
|
||||
|
||||
# 搜索注册表。
|
||||
npx shadcn@latest search @shadcn -q "sidebar"
|
||||
npx shadcn@latest search @tailark -q "stats"
|
||||
|
||||
# 获取组件文档和示例 URL。
|
||||
npx shadcn@latest docs button dialog select
|
||||
|
||||
# 查看注册表项详情(针对尚未安装的项)。
|
||||
npx shadcn@latest view @shadcn/button
|
||||
```
|
||||
|
||||
**命名预设:** `base-nova`、`radix-nova`
|
||||
**模板:** `next`、`vite`、`start`、`react-router`、`astro`(均支持 `--monorepo`)和 `laravel`(不支持单体仓库)
|
||||
**预设代码:** 以 `a` 开头的 Base62 字符串(例如 `a2r6bw`),来自 [ui.shadcn.com](https://ui.shadcn.com)。
|
||||
|
||||
## 详细参考
|
||||
|
||||
- [rules/forms.md](./rules/forms.md) — FieldGroup、Field、InputGroup、ToggleGroup、FieldSet、验证状态
|
||||
- [rules/composition.md](./rules/composition.md) — Groups、覆盖层、Card、Tabs、Avatar、Alert、Empty、Toast、Separator、Skeleton、Badge、Button 加载状态
|
||||
- [rules/icons.md](./rules/icons.md) — data-icon、图标尺寸、将图标作为对象传递
|
||||
- [rules/styling.md](./rules/styling.md) — 语义化颜色、变体、className、间距、尺寸、truncate、深色模式、cn()、z-index
|
||||
- [rules/base-vs-radix.md](./rules/base-vs-radix.md) — asChild 与 render、Select、ToggleGroup、Slider、Accordion
|
||||
- [cli.md](./cli.md) — 命令、标志、预设、模板
|
||||
- [customization.md](./customization.md) — 主题化、CSS 变量、扩展组件
|
||||
|
||||
## 限制
|
||||
- 仅当任务明确符合上述范围时使用此技能。
|
||||
- 请勿将输出视为环境特定验证、测试或专家审查的替代方案。
|
||||
- 如果缺少必要的输入、权限、安全边界或成功标准,请停下来并要求澄清。
|
||||
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "shadcn/ui"
|
||||
short_description: "Manages shadcn/ui components — adding, searching, fixing, debugging, styling, and composing UI."
|
||||
icon_small: "./assets/shadcn-small.png"
|
||||
icon_large: "./assets/shadcn.png"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,255 @@
|
||||
# shadcn CLI 参考
|
||||
|
||||
配置从 `components.json` 中读取。
|
||||
|
||||
> **重要说明:** 始终使用项目的包管理器来运行命令:`npx shadcn@latest`、`pnpm dlx shadcn@latest` 或 `bunx --bun shadcn@latest`。从项目上下文中检查 `packageManager` 以选择正确的命令。以下示例使用 `npx shadcn@latest`,但请替换为项目对应的正确运行器。
|
||||
|
||||
> **重要说明:** 只使用下面列出的标志。不要发明或猜测标志——如果某个标志未在此处列出,则它不存在。CLI 会从项目的锁定文件中自动检测包管理器;没有 `--package-manager` 标志。
|
||||
|
||||
## 目录
|
||||
|
||||
- 命令:init、add(空跑、智能合并)、search、view、docs、info、build
|
||||
- 模板:next、vite、start、react-router、astro
|
||||
- 预设:命名、代码、URL 格式与字段
|
||||
- 切换预设
|
||||
|
||||
---
|
||||
|
||||
## 命令
|
||||
|
||||
### `init` — 初始化或创建项目
|
||||
|
||||
```bash
|
||||
npx shadcn@latest init [components...] [options]
|
||||
```
|
||||
|
||||
在已有项目中初始化 shadcn/ui,或创建新项目(当提供了 `--name` 时)。可选地在同一步骤中安装组件。
|
||||
|
||||
| 标志 | 缩写 | 描述 | 默认值 |
|
||||
| ------------------------ | --- | -------------------------------------------------- | ------- |
|
||||
| `--template <template>` | `-t` | 模板(next、start、vite、next-monorepo、react-router) | — |
|
||||
| `--preset [name]` | `-p` | 预设配置(命名、代码或 URL) | — |
|
||||
| `--yes` | `-y` | 跳过确认提示 | `true` |
|
||||
| `--defaults` | `-d` | 使用默认值(`--template=next --preset=base-nova`) | `false` |
|
||||
| `--force` | `-f` | 强制覆盖现有配置 | `false` |
|
||||
| `--cwd <cwd>` | `-c` | 工作目录 | 当前目录 |
|
||||
| `--name <name>` | `-n` | 新项目的名称 | — |
|
||||
| `--silent` | `-s` | 静默输出 | `false` |
|
||||
| `--rtl` | | 启用 RTL 支持 | — |
|
||||
| `--reinstall` | | 重新安装现有 UI 组件 | `false` |
|
||||
| `--monorepo` | | 搭建 monorepo 项目骨架 | — |
|
||||
| `--no-monorepo` | | 跳过 monorepo 提示 | — |
|
||||
|
||||
`npx shadcn@latest create` 是 `npx shadcn@latest init` 的别名。
|
||||
|
||||
### `add` — 添加组件
|
||||
|
||||
> **重要说明:** 要比较本地组件与上游版本或预览更改,请**始终**使用 `npx shadcn@latest add <component> --dry-run`、`--diff` 或 `--view`。**切勿**从 GitHub 或其他来源手动获取原始文件。CLI 会自动处理注册表解析、文件路径和 CSS 差异比较。
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add [components...] [options]
|
||||
```
|
||||
|
||||
接受组件名称、注册表前缀名称(`@magicui/shimmer-button`)、URL 或本地路径。
|
||||
|
||||
| 标志 | 缩写 | 描述 | 默认值 |
|
||||
| ---------------------- | --- | ------------------------------------------------------------------------------- | --- |
|
||||
| `--yes` | `-y` | 跳过确认提示 | `false` |
|
||||
| `--overwrite` | `-o` | 覆盖现有文件 | `false` |
|
||||
| `--cwd <cwd>` | `-c` | 工作目录 | 当前目录 |
|
||||
| `--all` | `-a` | 添加所有可用组件 | `false` |
|
||||
| `--path <path>` | `-p` | 组件的目标路径 | — |
|
||||
| `--silent` | `-s` | 静默输出 | `false` |
|
||||
| `--dry-run` | | 预览所有更改但不写入文件 | `false` |
|
||||
| `--diff [path]` | | 显示差异。不带路径时显示前 5 个文件;带路径时只显示该文件(隐含 `--dry-run`) | — |
|
||||
| `--view [path]` | | 显示文件内容。不带路径时显示前 5 个文件;带路径时只显示该文件(隐含 `--dry-run`) | — |
|
||||
|
||||
#### 空跑模式
|
||||
|
||||
使用 `--dry-run` 预览 `add` 命令会做什么,而不实际写入任何文件。`--diff` 和 `--view` 都隐含 `--dry-run`。
|
||||
|
||||
```bash
|
||||
# 预览所有更改。
|
||||
npx shadcn@latest add button --dry-run
|
||||
|
||||
# 显示所有文件的差异(前 5 个)。
|
||||
npx shadcn@latest add button --diff
|
||||
|
||||
# 显示特定文件的差异。
|
||||
npx shadcn@latest add button --diff button.tsx
|
||||
|
||||
# 显示所有文件的内容(前 5 个)。
|
||||
npx shadcn@latest add button --view
|
||||
|
||||
# 显示特定文件的完整内容。
|
||||
npx shadcn@latest add button --view button.tsx
|
||||
|
||||
# URL 也同样支持。
|
||||
npx shadcn@latest add https://api.npoint.io/abc123 --dry-run
|
||||
|
||||
# CSS 差异。
|
||||
npx shadcn@latest add button --diff globals.css
|
||||
```
|
||||
|
||||
**何时使用空跑模式:**
|
||||
|
||||
- 当用户询问「这会添加哪些文件?」或「这会更改什么?」时——使用 `--dry-run`。
|
||||
- 在覆盖现有组件之前——使用 `--diff` 先预览更改。
|
||||
- 当用户想在不安装的情况下查看组件源码时——使用 `--view`。
|
||||
- 当需要检查 `globals.css` 会有哪些 CSS 更改时——使用 `--diff globals.css`。
|
||||
- 当用户要求在安装前审查或审核第三方注册表代码时——使用 `--view` 检查源代码。
|
||||
|
||||
> **`npx shadcn@latest add --dry-run` 与 `npx shadcn@latest view`:** 当用户想要预览更改对其项目的影响时,优先使用 `npx shadcn@latest add --dry-run/--diff/--view`,而不是 `npx shadcn@latest view`。`npx shadcn@latest view` 只显示原始注册表元数据。`npx shadcn@latest add --dry-run` 则精确显示在用户项目中会发生什么:解析后的文件路径、与现有文件的差异、以及 CSS 更新。只有当用户想在无项目上下文的情况下浏览注册表信息时,才使用 `npx shadcn@latest view`。
|
||||
|
||||
#### 上游智能合并
|
||||
|
||||
有关完整的工作流程,请参见 [SKILL.md 中的更新组件](./SKILL.md#updating-components)。
|
||||
|
||||
### `search` — 搜索注册表
|
||||
|
||||
```bash
|
||||
npx shadcn@latest search <registries...> [options]
|
||||
```
|
||||
|
||||
跨注册表进行模糊搜索。也支持别名 `npx shadcn@latest list`。不使用 `-q` 时列出所有项目。
|
||||
|
||||
| 标志 | 缩写 | 描述 | 默认值 |
|
||||
| ------------------------ | --- | ------------ | --- |
|
||||
| `--query <query>` | `-q` | 搜索查询 | — |
|
||||
| `--limit <number>` | `-l` | 每个注册表的最大项目数 | `100` |
|
||||
| `--offset <number>` | `-o` | 跳过的项目数 | `0` |
|
||||
| `--cwd <cwd>` | `-c` | 工作目录 | 当前目录 |
|
||||
|
||||
### `view` — 查看项目详情
|
||||
|
||||
```bash
|
||||
npx shadcn@latest view <items...> [options]
|
||||
```
|
||||
|
||||
显示项目信息,包括文件内容。示例:`npx shadcn@latest view @shadcn/button`。
|
||||
|
||||
### `docs` — 获取组件文档 URL
|
||||
|
||||
```bash
|
||||
npx shadcn@latest docs <components...> [options]
|
||||
```
|
||||
|
||||
输出组件文档、示例和 API 参考的解析后 URL。接受一个或多个组件名称。获取这些 URL 以得到实际内容。
|
||||
|
||||
`npx shadcn@latest docs input button` 的输出示例:
|
||||
|
||||
```
|
||||
base radix
|
||||
|
||||
input
|
||||
docs https://ui.shadcn.com/docs/components/radix/input
|
||||
examples https://raw.githubusercontent.com/.../examples/input-example.tsx
|
||||
|
||||
button
|
||||
docs https://ui.shadcn.com/docs/components/radix/button
|
||||
examples https://raw.githubusercontent.com/.../examples/button-example.tsx
|
||||
```
|
||||
|
||||
某些组件包含指向底层库的 `api` 链接(例如 command 组件指向 `cmdk`)。
|
||||
|
||||
### `diff` — 检查更新
|
||||
|
||||
不要使用此命令。请改用 `npx shadcn@latest add --diff`。
|
||||
|
||||
### `info` — 项目信息
|
||||
|
||||
```bash
|
||||
npx shadcn@latest info [options]
|
||||
```
|
||||
|
||||
显示项目信息和 `components.json` 配置。首先运行此命令以发现项目的框架、别名、Tailwind 版本和解析路径。
|
||||
|
||||
| 标志 | 缩写 | 描述 | 默认值 |
|
||||
| ---------------------- | --- | ------ | --- |
|
||||
| `--cwd <cwd>` | `-c` | 工作目录 | 当前目录 |
|
||||
|
||||
**项目信息字段:**
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
| -------------------- | --------- | --------------------------------------------------------- |
|
||||
| `framework` | `string` | 检测到的框架(`next`、`vite`、`react-router`、`start` 等) |
|
||||
| `frameworkVersion` | `string` | 框架版本(例如 `15.2.4`) |
|
||||
| `isSrcDir` | `boolean` | 项目是否使用 `src/` 目录 |
|
||||
| `isRSC` | `boolean` | 是否启用了 React Server Components |
|
||||
| `isTsx` | `boolean` | 项目是否使用 TypeScript |
|
||||
| `tailwindVersion` | `string` | `"v3"` 或 `"v4"` |
|
||||
| `tailwindConfigFile` | `string` | Tailwind 配置文件路径 |
|
||||
| `tailwindCssFile` | `string` | 全局 CSS 文件路径 |
|
||||
| `aliasPrefix` | `string` | 导入别名前缀(例如 `@`、`~`、`@/`) |
|
||||
| `packageManager` | `string` | 检测到的包管理器(`npm`、`pnpm`、`yarn`、`bun`) |
|
||||
|
||||
**Components.json 字段:**
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
| ----------------------- | --------- | ----------------------------------------------------- |
|
||||
| `base` | `string` | 基础库(`radix` 或 `base`)——决定组件 API 和可用属性 |
|
||||
| `style` | `string` | 视觉风格(例如 `nova`、`vega`) |
|
||||
| `rsc` | `boolean` | 来自配置的 RSC 标志 |
|
||||
| `tsx` | `boolean` | TypeScript 标志 |
|
||||
| `tailwind.config` | `string` | Tailwind 配置路径 |
|
||||
| `tailwind.css` | `string` | 全局 CSS 路径——这是放置自定义 CSS 变量的位置 |
|
||||
| `iconLibrary` | `string` | 图标库——决定图标导入包(例如 `lucide-react`、`@tabler/icons-react`) |
|
||||
| `aliases.components` | `string` | 组件导入别名(例如 `@/components`) |
|
||||
| `aliases.utils` | `string` | 工具导入别名(例如 `@/lib/utils`) |
|
||||
| `aliases.ui` | `string` | UI 组件别名(例如 `@/components/ui`) |
|
||||
| `aliases.lib` | `string` | Lib 别名(例如 `@/lib`) |
|
||||
| `aliases.hooks` | `string` | Hooks 别名(例如 `@/hooks`) |
|
||||
| `resolvedPaths` | `object` | 每个别名的绝对文件系统路径 |
|
||||
| `registries` | `object` | 配置的自定义注册表 |
|
||||
|
||||
**链接字段:**
|
||||
|
||||
`info` 输出包含一个**链接**部分,其中包含组件文档、源代码和示例的模板化 URL。对于解析后的 URL,请改用 `npx shadcn@latest docs <component>`。
|
||||
|
||||
### `build` — 构建自定义注册表
|
||||
|
||||
```bash
|
||||
npx shadcn@latest build [registry] [options]
|
||||
```
|
||||
|
||||
将 `registry.json` 构建为单个 JSON 文件以进行分发。默认输入:`./registry.json`,默认输出:`./public/r`。
|
||||
|
||||
| 标志 | 缩写 | 描述 | 默认值 |
|
||||
| ------------------------ | --- | ------ | ------------ |
|
||||
| `--output <path>` | `-o` | 输出目录 | `./public/r` |
|
||||
| `--cwd <cwd>` | `-c` | 工作目录 | 当前目录 |
|
||||
|
||||
---
|
||||
|
||||
## 模板
|
||||
|
||||
| 值 | 框架 | 支持 Monorepo |
|
||||
| --------------- | --------------- | ----------- |
|
||||
| `next` | Next.js | 是 |
|
||||
| `vite` | Vite | 是 |
|
||||
| `start` | TanStack Start | 是 |
|
||||
| `react-router` | React Router | 是 |
|
||||
| `astro` | Astro | 是 |
|
||||
| `laravel` | Laravel | 否 |
|
||||
|
||||
所有模板均通过 `--monorepo` 标志支持 monorepo 脚手架。当传入该标志时,CLI 使用 monorepo 专用的模板目录(例如 `next-monorepo`、`vite-monorepo`)。当既不传递 `--monorepo` 也不传递 `--no-monorepo` 时,CLI 会交互式地提示。Laravel 不支持 monorepo 脚手架。
|
||||
|
||||
---
|
||||
|
||||
## 预设
|
||||
|
||||
通过 `--preset` 指定预设的三种方式:
|
||||
|
||||
1. **命名:** `--preset base-nova` 或 `--preset radix-nova`
|
||||
2. **代码:** `--preset a2r6bw`(base62 字符串,以小写字母 `a` 开头)
|
||||
3. **URL:** `--preset "https://ui.shadcn.com/init?base=radix&style=nova&..."`
|
||||
|
||||
> **重要说明:** 切勿尝试手动解码、获取或解析预设代码。预设代码是不透明的——直接将它们传给 `npx shadcn@latest init --preset <code>`,让 CLI 处理解析。
|
||||
|
||||
## 切换预设
|
||||
|
||||
先询问用户:**重新安装**、**合并**还是**跳过**现有组件?
|
||||
|
||||
- **重新安装** → `npx shadcn@latest init --preset <code> --force --reinstall`。用新的预设样式覆盖所有组件文件。在用户未自定义过组件时使用。
|
||||
- **合并** → `npx shadcn@latest init --preset <code> --force --no-reinstall`,然后运行 `npx shadcn@latest info` 获取已安装组件列表,并使用[智能合并工作流程](./SKILL.md#updating-components)逐一更新它们,同时保留本地更改。在用户已自定义过组件时使用。
|
||||
- **跳过** → `npx shadcn@latest init --preset <code> --force --no-reinstall`。仅更新配置和 CSS 变量,保持现有组件不变。
|
||||
@@ -0,0 +1,209 @@
|
||||
---
|
||||
name: customization-theming
|
||||
description: shadcn/ui 定制与主题系统文档
|
||||
metadata:
|
||||
type: reference
|
||||
---
|
||||
|
||||
# 定制与主题
|
||||
|
||||
组件引用语义化的 CSS 变量令牌。修改变量即可更改所有组件。
|
||||
|
||||
## 目录
|
||||
|
||||
- 工作原理(CSS 变量 → Tailwind 工具类 → 组件)
|
||||
- 颜色变量与 OKLCH 格式
|
||||
- 深色模式设置
|
||||
- 更改主题(预设、CSS 变量)
|
||||
- 添加自定义颜色(Tailwind v3 和 v4)
|
||||
- 边框圆角
|
||||
- 定制组件(变体、className、包装器)
|
||||
- 检查更新
|
||||
|
||||
---
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. 在 `:root`(浅色)和 `.dark`(深色模式)中定义 CSS 变量。
|
||||
2. Tailwind 将其映射为工具类:`bg-primary`、`text-muted-foreground` 等。
|
||||
3. 组件使用这些工具类——更改某个变量即可更改所有引用它的组件。
|
||||
|
||||
---
|
||||
|
||||
## 颜色变量
|
||||
|
||||
每种颜色遵循 `name` / `name-foreground` 命名约定。基础变量用于背景,`-foreground` 用于该背景上的文本/图标。
|
||||
|
||||
| 变量 | 用途 |
|
||||
| -------------------------------------------- | -------------------------------- |
|
||||
| `--background` / `--foreground` | 页面背景与默认文本 |
|
||||
| `--card` / `--card-foreground` | 卡片表面 |
|
||||
| `--primary` / `--primary-foreground` | 主要按钮与操作 |
|
||||
| `--secondary` / `--secondary-foreground` | 次要操作 |
|
||||
| `--muted` / `--muted-foreground` | 弱化/禁用状态 |
|
||||
| `--accent` / `--accent-foreground` | 悬停与强调状态 |
|
||||
| `--destructive` / `--destructive-foreground` | 错误与破坏性操作 |
|
||||
| `--border` | 默认边框颜色 |
|
||||
| `--input` | 表单输入边框 |
|
||||
| `--ring` | 聚焦环颜色 |
|
||||
| `--chart-1` 至 `--chart-5` | 图表/数据可视化 |
|
||||
| `--sidebar-*` | 侧边栏专用颜色 |
|
||||
| `--surface` / `--surface-foreground` | 次级表面 |
|
||||
|
||||
颜色使用 OKLCH 格式:`--primary: oklch(0.205 0 0)`,其中各值分别为明度(0–1)、色度(0 = 灰色)和色相(0–360)。
|
||||
|
||||
---
|
||||
|
||||
## 深色模式
|
||||
|
||||
通过根元素上的 `.dark` 类进行切换。在 Next.js 中,使用 `next-themes`:
|
||||
|
||||
```tsx
|
||||
import { ThemeProvider } from "next-themes"
|
||||
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 更改主题
|
||||
|
||||
```bash
|
||||
# 应用来自 ui.shadcn.com 的预设代码。
|
||||
npx shadcn@latest init --preset a2r6bw --force
|
||||
|
||||
# 切换到命名预设。
|
||||
npx shadcn@latest init --preset radix-nova --force
|
||||
npx shadcn@latest init --reinstall # 更新现有组件以匹配
|
||||
|
||||
# 使用自定义主题 URL。
|
||||
npx shadcn@latest init --preset "https://ui.shadcn.com/init?base=radix&style=nova&theme=blue&..." --force
|
||||
```
|
||||
|
||||
或者直接在 `globals.css` 中编辑 CSS 变量。
|
||||
|
||||
---
|
||||
|
||||
## 添加自定义颜色
|
||||
|
||||
将变量添加到 `npx shadcn@latest info` 返回的 `tailwindCssFile` 所指向的文件中(通常为 `globals.css`)。切勿为此创建新的 CSS 文件。
|
||||
|
||||
```css
|
||||
/* 1. 在全局 CSS 文件中定义。 */
|
||||
:root {
|
||||
--warning: oklch(0.84 0.16 84);
|
||||
--warning-foreground: oklch(0.28 0.07 46);
|
||||
}
|
||||
.dark {
|
||||
--warning: oklch(0.41 0.11 46);
|
||||
--warning-foreground: oklch(0.99 0.02 95);
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/* 2a. 在 Tailwind v4 中注册(@theme inline)。 */
|
||||
@theme inline {
|
||||
--color-warning: var(--warning);
|
||||
--color-warning-foreground: var(--warning-foreground);
|
||||
}
|
||||
```
|
||||
|
||||
当 `tailwindVersion` 为 `"v3"` 时(通过 `npx shadcn@latest info` 检查),改为在 `tailwind.config.js` 中注册:
|
||||
|
||||
```js
|
||||
// 2b. 在 Tailwind v3 中注册(tailwind.config.js)。
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
warning: "oklch(var(--warning) / <alpha-value>)",
|
||||
"warning-foreground":
|
||||
"oklch(var(--warning-foreground) / <alpha-value>)",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// 3. 在组件中使用。
|
||||
<div className="bg-warning text-warning-foreground">Warning</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 边框圆角
|
||||
|
||||
`--radius` 全局控制边框圆角。组件从其派生值(`rounded-lg` = `var(--radius)`,`rounded-md` = `calc(var(--radius) - 2px)`)。
|
||||
|
||||
---
|
||||
|
||||
## 定制组件
|
||||
|
||||
另请参阅:[rules/styling.md](./rules/styling.md) 中的错误/正确示例。
|
||||
|
||||
按以下顺序优先选择这些方法:
|
||||
|
||||
### 1. 内置变体
|
||||
|
||||
```tsx
|
||||
<Button variant="outline" size="sm">Click</Button>
|
||||
```
|
||||
|
||||
### 2. 通过 `className` 使用 Tailwind 类
|
||||
|
||||
```tsx
|
||||
<Card className="max-w-md mx-auto">...</Card>
|
||||
```
|
||||
|
||||
### 3. 添加新变体
|
||||
|
||||
编辑组件源码,通过 `cva` 添加变体:
|
||||
|
||||
```tsx
|
||||
// components/ui/button.tsx
|
||||
warning: "bg-warning text-warning-foreground hover:bg-warning/90",
|
||||
```
|
||||
|
||||
### 4. 包装器组件
|
||||
|
||||
将 shadcn/ui 基础组件组合成更高级的组件:
|
||||
|
||||
```tsx
|
||||
export function ConfirmDialog({ title, description, onConfirm, children }) {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{title}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{description}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onConfirm}>Confirm</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 检查更新
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add button --diff
|
||||
```
|
||||
|
||||
要在更新前预览具体变化,请使用 `--dry-run` 和 `--diff`:
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add button --dry-run # 查看所有受影响的文件
|
||||
npx shadcn@latest add button --diff button.tsx # 查看特定文件的差异
|
||||
```
|
||||
|
||||
完整智能合并工作流请参阅 [SKILL.md 中的更新组件](./SKILL.md#updating-components)。
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"skill_name": "shadcn",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"prompt": "I'm building a Next.js app with shadcn/ui (base-nova preset, lucide icons). Create a settings form component with fields for: full name, email address, and notification preferences (email, SMS, push notifications as toggle options). Add validation states for required fields.",
|
||||
"expected_output": "A React component using FieldGroup, Field, ToggleGroup, data-invalid/aria-invalid validation, gap-* spacing, and semantic colors.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"Uses FieldGroup and Field components for form layout instead of raw div with space-y",
|
||||
"Uses Switch for independent on/off notification toggles (not looping Button with manual active state)",
|
||||
"Uses data-invalid on Field and aria-invalid on the input control for validation states",
|
||||
"Uses gap-* (e.g. gap-4, gap-6) instead of space-y-* or space-x-* for spacing",
|
||||
"Uses semantic color tokens (e.g. bg-background, text-muted-foreground, text-destructive) instead of raw colors like bg-red-500",
|
||||
"No manual dark: color overrides"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"prompt": "Create a dialog component for editing a user profile. It should have the user's avatar at the top, input fields for name and bio, and Save/Cancel buttons with appropriate icons. Using shadcn/ui with radix-nova preset and tabler icons.",
|
||||
"expected_output": "A React component with DialogTitle, Avatar+AvatarFallback, data-icon on icon buttons, no icon sizing classes, tabler icon imports.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"Includes DialogTitle for accessibility (visible or with sr-only class)",
|
||||
"Avatar component includes AvatarFallback",
|
||||
"Icons on buttons use the data-icon attribute (data-icon=\"inline-start\" or data-icon=\"inline-end\")",
|
||||
"No sizing classes on icons inside components (no size-4, w-4, h-4, etc.)",
|
||||
"Uses tabler icons (@tabler/icons-react) instead of lucide-react",
|
||||
"Uses asChild for custom triggers (radix preset)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"prompt": "Create a dashboard component that shows 4 stat cards in a grid. Each card has a title, large number, percentage change badge, and a loading skeleton state. Using shadcn/ui with base-nova preset and lucide icons.",
|
||||
"expected_output": "A React component with full Card composition, Skeleton for loading, Badge for changes, semantic colors, gap-* spacing.",
|
||||
"files": [],
|
||||
"expectations": [
|
||||
"Uses full Card composition with CardHeader, CardTitle, CardContent (not dumping everything into CardContent)",
|
||||
"Uses Skeleton component for loading placeholders instead of custom animate-pulse divs",
|
||||
"Uses Badge component for percentage change instead of custom styled spans",
|
||||
"Uses semantic color tokens instead of raw color values like bg-green-500 or text-red-600",
|
||||
"Uses gap-* instead of space-y-* or space-x-* for spacing",
|
||||
"Uses size-* when width and height are equal instead of separate w-* h-*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
# shadcn MCP Server
|
||||
|
||||
CLI 包含一个 MCP 服务器,允许 AI 助手从注册表中搜索、浏览、查看和安装组件。
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
shadcn mcp # 启动 MCP 服务器(stdio 模式)
|
||||
shadcn mcp init # 为编辑器写入配置文件
|
||||
```
|
||||
|
||||
编辑器配置文件:
|
||||
|
||||
| 编辑器 | 配置文件 |
|
||||
|--------|------------|
|
||||
| Claude Code | `.mcp.json` |
|
||||
| Cursor | `.cursor/mcp.json` |
|
||||
| VS Code | `.vscode/mcp.json` |
|
||||
| OpenCode | `opencode.json` |
|
||||
| Codex | `~/.codex/config.toml`(手动配置) |
|
||||
|
||||
---
|
||||
|
||||
## 工具
|
||||
|
||||
> **提示:** MCP 工具处理注册表操作(搜索、查看、安装)。对于项目配置(别名、框架、Tailwind 版本),请使用 `npx shadcn@latest info` —— 没有对应的 MCP 命令。
|
||||
|
||||
### `shadcn:get_project_registries`
|
||||
|
||||
返回 `components.json` 中的注册表名称。如果不存在 `components.json` 则报错。
|
||||
|
||||
**输入:** 无
|
||||
|
||||
### `shadcn:list_items_in_registries`
|
||||
|
||||
列出一个或多个注册表中的所有条目。
|
||||
|
||||
**输入:** `registries`(string[]),`limit`(number,可选),`offset`(number,可选)
|
||||
|
||||
### `shadcn:search_items_in_registries`
|
||||
|
||||
跨注册表进行模糊搜索。
|
||||
|
||||
**输入:** `registries`(string[]),`query`(string),`limit`(number,可选),`offset`(number,可选)
|
||||
|
||||
### `shadcn:view_items_in_registries`
|
||||
|
||||
查看条目详情,包含完整的文件内容。
|
||||
|
||||
**输入:** `items`(string[])—— 例如 `["@shadcn/button", "@shadcn/card"]`
|
||||
|
||||
### `shadcn:get_item_examples_from_registries`
|
||||
|
||||
查找包含源代码的使用示例和演示。
|
||||
|
||||
**输入:** `registries`(string[]),`query`(string)—— 例如 `"accordion-demo"`、`"button example"`
|
||||
|
||||
### `shadcn:get_add_command_for_items`
|
||||
|
||||
返回 CLI 安装命令。
|
||||
|
||||
**输入:** `items`(string[])—— 例如 `["@shadcn/button"]`
|
||||
|
||||
### `shadcn:get_audit_checklist`
|
||||
|
||||
返回用于验证组件(导入、依赖、lint、TypeScript)的检查清单。
|
||||
|
||||
**输入:** 无
|
||||
|
||||
---
|
||||
|
||||
## 配置注册表
|
||||
|
||||
注册表在 `components.json` 中设置。`@shadcn` 注册表始终内置。
|
||||
|
||||
```json
|
||||
{
|
||||
"registries": {
|
||||
"@acme": "https://acme.com/r/{name}.json",
|
||||
"@private": {
|
||||
"url": "https://private.com/r/{name}.json",
|
||||
"headers": { "Authorization": "Bearer ${MY_TOKEN}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 名称必须以 `@` 开头。
|
||||
- URL 必须包含 `{name}`。
|
||||
- `${VAR}` 引用从环境变量中解析。
|
||||
|
||||
社区注册表索引:`https://ui.shadcn.com/r/registries.json`
|
||||
@@ -0,0 +1,306 @@
|
||||
# Base vs Radix
|
||||
|
||||
`base` 与 `radix` 之间的 API 差异。请通过 `npx shadcn@latest info` 检查 `base` 字段。
|
||||
|
||||
## 目录
|
||||
|
||||
- 组合方式:asChild 与 render
|
||||
- 按钮/触发器作为非按钮元素
|
||||
- Select(items 属性、占位符、定位、多选、对象值)
|
||||
- ToggleGroup(type 与 multiple)
|
||||
- Slider(标量与数组)
|
||||
- Accordion(type 与 defaultValue)
|
||||
|
||||
---
|
||||
|
||||
## 组合方式:asChild(radix)与 render(base)
|
||||
|
||||
Radix 使用 `asChild` 来替换默认元素。Base 使用 `render`。不要在触发器外层包裹多余元素。
|
||||
|
||||
**错误用法:**
|
||||
|
||||
```tsx
|
||||
<DialogTrigger>
|
||||
<div>
|
||||
<Button>Open</Button>
|
||||
</div>
|
||||
</DialogTrigger>
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
<DialogTrigger asChild>
|
||||
<Button>Open</Button>
|
||||
</DialogTrigger>
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
<DialogTrigger render={<Button />}>Open</DialogTrigger>
|
||||
```
|
||||
|
||||
这适用于所有触发器和关闭组件:`DialogTrigger`、`SheetTrigger`、`AlertDialogTrigger`、`DropdownMenuTrigger`、`PopoverTrigger`、`TooltipTrigger`、`CollapsibleTrigger`、`DialogClose`、`SheetClose`、`NavigationMenuLink`、`BreadcrumbLink`、`SidebarMenuButton`、`Badge`、`Item`。
|
||||
|
||||
---
|
||||
|
||||
## 按钮/触发器作为非按钮元素(仅 base)
|
||||
|
||||
当 `render` 将元素改为非按钮元素(`<a>`、`<span>`)时,需添加 `nativeButton={false}`。
|
||||
|
||||
**错误用法(base):** 缺少 `nativeButton={false}`。
|
||||
|
||||
```tsx
|
||||
<Button render={<a href="/docs" />}>Read the docs</Button>
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
<Button render={<a href="/docs" />} nativeButton={false}>
|
||||
Read the docs
|
||||
</Button>
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
<Button asChild>
|
||||
<a href="/docs">Read the docs</a>
|
||||
</Button>
|
||||
```
|
||||
|
||||
对于 `render` 不是 `Button` 的触发器同样适用:
|
||||
|
||||
```tsx
|
||||
// base.
|
||||
<PopoverTrigger render={<InputGroupAddon />} nativeButton={false}>
|
||||
Pick date
|
||||
</PopoverTrigger>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Select
|
||||
|
||||
**items 属性(仅 base)。** Base 需要在根组件上提供 `items` 属性。Radix 仅使用内联 JSX。
|
||||
|
||||
**错误用法(base):**
|
||||
|
||||
```tsx
|
||||
<Select>
|
||||
<SelectTrigger><SelectValue placeholder="Select a fruit" /></SelectTrigger>
|
||||
</Select>
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
const items = [
|
||||
{ label: "Select a fruit", value: null },
|
||||
{ label: "Apple", value: "apple" },
|
||||
{ label: "Banana", value: "banana" },
|
||||
]
|
||||
|
||||
<Select items={items}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{items.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>{item.label}</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
<Select>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="apple">Apple</SelectItem>
|
||||
<SelectItem value="banana">Banana</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
```
|
||||
|
||||
**占位符。** Base 在 items 数组中使用 `{ value: null }` 项。Radix 使用 `<SelectValue placeholder="...">`。
|
||||
|
||||
**内容定位。** Base 使用 `alignItemWithTrigger`。Radix 使用 `position`。
|
||||
|
||||
```tsx
|
||||
// base.
|
||||
<SelectContent alignItemWithTrigger={false} side="bottom">
|
||||
|
||||
// radix.
|
||||
<SelectContent position="popper">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Select——多选与对象值(仅 base)
|
||||
|
||||
Base 支持 `multiple`、`SelectValue` 上的 render 函数子元素,以及通过 `itemToStringValue` 实现的对象值。Radix 仅支持单选和字符串值。
|
||||
|
||||
**正确用法(base——多选):**
|
||||
|
||||
```tsx
|
||||
<Select items={items} multiple defaultValue={[]}>
|
||||
<SelectTrigger>
|
||||
<SelectValue>
|
||||
{(value: string[]) => value.length === 0 ? "Select fruits" : `${value.length} selected`}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
...
|
||||
</Select>
|
||||
```
|
||||
|
||||
**正确用法(base——对象值):**
|
||||
|
||||
```tsx
|
||||
<Select defaultValue={plans[0]} itemToStringValue={(plan) => plan.name}>
|
||||
<SelectTrigger>
|
||||
<SelectValue>{(value) => value.name}</SelectValue>
|
||||
</SelectTrigger>
|
||||
...
|
||||
</Select>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ToggleGroup
|
||||
|
||||
Base 使用布尔类型的 `multiple` 属性。Radix 使用 `type="single"` 或 `type="multiple"`。
|
||||
|
||||
**错误用法(base):**
|
||||
|
||||
```tsx
|
||||
<ToggleGroup type="single" defaultValue="daily">
|
||||
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
// 单选(无需属性),defaultValue 始终为数组。
|
||||
<ToggleGroup defaultValue={["daily"]} spacing={2}>
|
||||
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
|
||||
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
|
||||
// 多选。
|
||||
<ToggleGroup multiple>
|
||||
<ToggleGroupItem value="bold">Bold</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic">Italic</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
// 单选,defaultValue 为字符串。
|
||||
<ToggleGroup type="single" defaultValue="daily" spacing={2}>
|
||||
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
|
||||
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
|
||||
// 多选。
|
||||
<ToggleGroup type="multiple">
|
||||
<ToggleGroupItem value="bold">Bold</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic">Italic</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
**受控模式下的单选值:**
|
||||
|
||||
```tsx
|
||||
// base——对数组进行包裹/解包。
|
||||
const [value, setValue] = React.useState("normal")
|
||||
<ToggleGroup value={[value]} onValueChange={(v) => setValue(v[0])}>
|
||||
|
||||
// radix——纯字符串。
|
||||
const [value, setValue] = React.useState("normal")
|
||||
<ToggleGroup type="single" value={value} onValueChange={setValue}>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Slider
|
||||
|
||||
Base 对单滑块接受纯数字。Radix 始终要求使用数组。
|
||||
|
||||
**错误用法(base):**
|
||||
|
||||
```tsx
|
||||
<Slider defaultValue={[50]} max={100} step={1} />
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
<Slider defaultValue={50} max={100} step={1} />
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
<Slider defaultValue={[50]} max={100} step={1} />
|
||||
```
|
||||
|
||||
两者在范围滑块上都使用数组。Base 中的受控 `onValueChange` 可能需要类型断言:
|
||||
|
||||
```tsx
|
||||
// base.
|
||||
const [value, setValue] = React.useState([0.3, 0.7])
|
||||
<Slider value={value} onValueChange={(v) => setValue(v as number[])} />
|
||||
|
||||
// radix.
|
||||
const [value, setValue] = React.useState([0.3, 0.7])
|
||||
<Slider value={value} onValueChange={setValue} />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accordion
|
||||
|
||||
Radix 要求使用 `type="single"` 或 `type="multiple"`,并支持 `collapsible`。`defaultValue` 为字符串。Base 不使用 `type` 属性,使用布尔类型的 `multiple`,且 `defaultValue` 始终为数组。
|
||||
|
||||
**错误用法(base):**
|
||||
|
||||
```tsx
|
||||
<Accordion type="single" collapsible defaultValue="item-1">
|
||||
<AccordionItem value="item-1">...</AccordionItem>
|
||||
</Accordion>
|
||||
```
|
||||
|
||||
**正确用法(base):**
|
||||
|
||||
```tsx
|
||||
<Accordion defaultValue={["item-1"]}>
|
||||
<AccordionItem value="item-1">...</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
// 多选。
|
||||
<Accordion multiple defaultValue={["item-1", "item-2"]}>
|
||||
<AccordionItem value="item-1">...</AccordionItem>
|
||||
<AccordionItem value="item-2">...</AccordionItem>
|
||||
</Accordion>
|
||||
```
|
||||
|
||||
**正确用法(radix):**
|
||||
|
||||
```tsx
|
||||
<Accordion type="single" collapsible defaultValue="item-1">
|
||||
<AccordionItem value="item-1">...</AccordionItem>
|
||||
</Accordion>
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
# 组件组合
|
||||
|
||||
## 目录
|
||||
|
||||
- 条目始终位于其 Group 组件内
|
||||
- 标注提示使用 Alert
|
||||
- 空状态使用 Empty 组件
|
||||
- Toast 通知使用 sonner
|
||||
- 如何选择覆盖层组件
|
||||
- Dialog、Sheet 和 Drawer 始终需要 Title
|
||||
- Card 结构
|
||||
- Button 没有 isPending 或 isLoading 属性
|
||||
- TabsTrigger 必须位于 TabsList 内部
|
||||
- Avatar 始终需要 AvatarFallback
|
||||
- 使用 Separator 替代原生 hr 或 border div
|
||||
- 使用 Skeleton 作为加载占位符
|
||||
- 使用 Badge 替代自定义样式 span
|
||||
|
||||
---
|
||||
|
||||
## 条目始终位于其 Group 组件内
|
||||
|
||||
切勿直接在内容容器内渲染条目。
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<SelectContent>
|
||||
<SelectItem value="apple">Apple</SelectItem>
|
||||
<SelectItem value="banana">Banana</SelectItem>
|
||||
</SelectContent>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="apple">Apple</SelectItem>
|
||||
<SelectItem value="banana">Banana</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
```
|
||||
|
||||
这适用于所有基于 Group 的组件:
|
||||
|
||||
| 条目 | 分组容器 |
|
||||
|------|---------|
|
||||
| `SelectItem`, `SelectLabel` | `SelectGroup` |
|
||||
| `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSub` | `DropdownMenuGroup` |
|
||||
| `MenubarItem` | `MenubarGroup` |
|
||||
| `ContextMenuItem` | `ContextMenuGroup` |
|
||||
| `CommandItem` | `CommandGroup` |
|
||||
|
||||
---
|
||||
|
||||
## 标注提示使用 Alert
|
||||
|
||||
```tsx
|
||||
<Alert>
|
||||
<AlertTitle>警告</AlertTitle>
|
||||
<AlertDescription>某些内容需要关注。</AlertDescription>
|
||||
</Alert>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 空状态使用 Empty 组件
|
||||
|
||||
```tsx
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon"><FolderIcon /></EmptyMedia>
|
||||
<EmptyTitle>暂无项目</EmptyTitle>
|
||||
<EmptyDescription>立即创建一个新项目开始使用。</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button>创建项目</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Toast 通知使用 sonner
|
||||
|
||||
```tsx
|
||||
import { toast } from "sonner"
|
||||
|
||||
toast.success("更改已保存。")
|
||||
toast.error("发生错误。")
|
||||
toast("文件已删除。", {
|
||||
action: { label: "撤销", onClick: () => undoDelete() },
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 如何选择覆盖层组件
|
||||
|
||||
| 使用场景 | 组件 |
|
||||
|----------|------|
|
||||
| 需要用户输入的专注型任务 | `Dialog` |
|
||||
| 破坏性操作确认 | `AlertDialog` |
|
||||
| 显示详情或筛选条件的侧面板 | `Sheet` |
|
||||
| 移动端优先的底部面板 | `Drawer` |
|
||||
| 悬停时快速查看信息 | `HoverCard` |
|
||||
| 点击后显示小型上下文内容 | `Popover` |
|
||||
|
||||
---
|
||||
|
||||
## Dialog、Sheet 和 Drawer 始终需要 Title
|
||||
|
||||
`DialogTitle`、`SheetTitle`、`DrawerTitle` 是无障碍所必需的。如果希望视觉上隐藏,请使用 `className="sr-only"`。
|
||||
|
||||
```tsx
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>编辑个人资料</DialogTitle>
|
||||
<DialogDescription>更新您的个人资料。</DialogDescription>
|
||||
</DialogHeader>
|
||||
...
|
||||
</DialogContent>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Card 结构
|
||||
|
||||
使用完整的组合方式——不要把所有内容都塞进 `CardContent`:
|
||||
|
||||
```tsx
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>团队成员</CardTitle>
|
||||
<CardDescription>管理您的团队。</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>...</CardContent>
|
||||
<CardFooter>
|
||||
<Button>邀请</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Button 没有 isPending 或 isLoading 属性
|
||||
|
||||
配合 `Spinner` + `data-icon` + `disabled` 进行组合:
|
||||
|
||||
```tsx
|
||||
<Button disabled>
|
||||
<Spinner data-icon="inline-start" />
|
||||
保存中...
|
||||
</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TabsTrigger 必须位于 TabsList 内部
|
||||
|
||||
切勿在 `Tabs` 内部直接渲染 `TabsTrigger`——始终包裹在 `TabsList` 中:
|
||||
|
||||
```tsx
|
||||
<Tabs defaultValue="account">
|
||||
<TabsList>
|
||||
<TabsTrigger value="account">账户</TabsTrigger>
|
||||
<TabsTrigger value="password">密码</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="account">...</TabsContent>
|
||||
</Tabs>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Avatar 始终需要 AvatarFallback
|
||||
|
||||
始终包含 `AvatarFallback`,用于图片加载失败时的降级显示:
|
||||
|
||||
```tsx
|
||||
<Avatar>
|
||||
<AvatarImage src="/avatar.png" alt="用户" />
|
||||
<AvatarFallback>JD</AvatarFallback>
|
||||
</Avatar>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用现有组件替代自定义标记
|
||||
|
||||
| 不要使用 | 请使用 |
|
||||
|---|---|
|
||||
| `<hr>` 或 `<div className="border-t">` | `<Separator />` |
|
||||
| `<div className="animate-pulse">` 配合样式 div | `<Skeleton className="h-4 w-3/4" />` |
|
||||
| `<span className="rounded-full bg-green-100 ...">` | `<Badge variant="secondary">` |
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
# 表单与输入
|
||||
|
||||
## 目录
|
||||
|
||||
- 表单使用 FieldGroup + Field
|
||||
- InputGroup 需配合 InputGroupInput/InputGroupTextarea
|
||||
- 输入框内的按钮使用 InputGroup + InputGroupAddon
|
||||
- 选项集合(2–7 个选项)使用 ToggleGroup
|
||||
- FieldSet + FieldLegend 用于对相关字段进行分组
|
||||
- 字段校验与禁用状态
|
||||
|
||||
---
|
||||
|
||||
## 表单使用 FieldGroup + Field
|
||||
|
||||
始终使用 `FieldGroup` + `Field` —— 切勿使用 `space-y-*` 的原始 `div`:
|
||||
|
||||
```tsx
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">邮箱</FieldLabel>
|
||||
<Input id="email" type="email" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="password">密码</FieldLabel>
|
||||
<Input id="password" type="password" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
```
|
||||
|
||||
在设置页面中使用 `Field orientation="horizontal"`。对于视觉上隐藏的标签,使用 `FieldLabel className="sr-only"`。
|
||||
|
||||
**选择表单控件:**
|
||||
|
||||
- 简单文本输入 → `Input`
|
||||
- 带预定义选项的下拉框 → `Select`
|
||||
- 可搜索下拉框 → `Combobox`
|
||||
- 原生 HTML 选择(无 JS)→ `native-select`
|
||||
- 布尔切换 → `Switch`(用于设置)或 `Checkbox`(用于表单)
|
||||
- 少数选项中的单选 → `RadioGroup`
|
||||
- 在 2–5 个选项之间切换 → `ToggleGroup` + `ToggleGroupItem`
|
||||
- OTP/验证码 → `InputOTP`
|
||||
- 多行文本 → `Textarea`
|
||||
|
||||
---
|
||||
|
||||
## InputGroup 需配合 InputGroupInput/InputGroupTextarea
|
||||
|
||||
切勿在 `InputGroup` 内部使用原始的 `Input` 或 `Textarea`。
|
||||
|
||||
**错误示例:**
|
||||
|
||||
```tsx
|
||||
<InputGroup>
|
||||
<Input placeholder="搜索..." />
|
||||
</InputGroup>
|
||||
```
|
||||
|
||||
**正确示例:**
|
||||
|
||||
```tsx
|
||||
import { InputGroup, InputGroupInput } from "@/components/ui/input-group"
|
||||
|
||||
<InputGroup>
|
||||
<InputGroupInput placeholder="搜索..." />
|
||||
</InputGroup>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 输入框内的按钮使用 InputGroup + InputGroupAddon
|
||||
|
||||
切勿通过自定义定位将 `Button` 直接放在 `Input` 内部或紧邻其旁。
|
||||
|
||||
**错误示例:**
|
||||
|
||||
```tsx
|
||||
<div className="relative">
|
||||
<Input placeholder="搜索..." className="pr-10" />
|
||||
<Button className="absolute right-0 top-0" size="icon">
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
**正确示例:**
|
||||
|
||||
```tsx
|
||||
import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group"
|
||||
|
||||
<InputGroup>
|
||||
<InputGroupInput placeholder="搜索..." />
|
||||
<InputGroupAddon>
|
||||
<Button size="icon">
|
||||
<SearchIcon data-icon="inline-start" />
|
||||
</Button>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 选项集合(2–7 个选项)使用 ToggleGroup
|
||||
|
||||
不要手动循环 `Button` 组件并自行管理激活状态。
|
||||
|
||||
**错误示例:**
|
||||
|
||||
```tsx
|
||||
const [selected, setSelected] = useState("daily")
|
||||
|
||||
<div className="flex gap-2">
|
||||
{["daily", "weekly", "monthly"].map((option) => (
|
||||
<Button
|
||||
key={option}
|
||||
variant={selected === option ? "default" : "outline"}
|
||||
onClick={() => setSelected(option)}
|
||||
>
|
||||
{option}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
```
|
||||
|
||||
**正确示例:**
|
||||
|
||||
```tsx
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
||||
|
||||
<ToggleGroup spacing={2}>
|
||||
<ToggleGroupItem value="daily">每日</ToggleGroupItem>
|
||||
<ToggleGroupItem value="weekly">每周</ToggleGroupItem>
|
||||
<ToggleGroupItem value="monthly">每月</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
```
|
||||
|
||||
结合 `Field` 使用,创建带标签的切换组:
|
||||
|
||||
```tsx
|
||||
<Field orientation="horizontal">
|
||||
<FieldTitle id="theme-label">主题</FieldTitle>
|
||||
<ToggleGroup aria-labelledby="theme-label" spacing={2}>
|
||||
<ToggleGroupItem value="light">浅色</ToggleGroupItem>
|
||||
<ToggleGroupItem value="dark">深色</ToggleGroupItem>
|
||||
<ToggleGroupItem value="system">跟随系统</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</Field>
|
||||
```
|
||||
|
||||
> **注意:** `defaultValue` 和 `type`/`multiple` 属性在 base 与 radix 之间存在差异。详见 [base-vs-radix.md](./base-vs-radix.md#togglegroup)。
|
||||
|
||||
---
|
||||
|
||||
## FieldSet + FieldLegend 用于对相关字段进行分组
|
||||
|
||||
对相关的复选框、单选按钮或开关,使用 `FieldSet` + `FieldLegend` —— 而不是在 `div` 中加一个标题:
|
||||
|
||||
```tsx
|
||||
<FieldSet>
|
||||
<FieldLegend variant="label">偏好设置</FieldLegend>
|
||||
<FieldDescription>请选择所有适用的选项。</FieldDescription>
|
||||
<FieldGroup className="gap-3">
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="dark" />
|
||||
<FieldLabel htmlFor="dark" className="font-normal">深色模式</FieldLabel>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 字段校验与禁用状态
|
||||
|
||||
两个属性都需要 —— `data-invalid`/`data-disabled` 用于设置字段的样式(标签、描述),而 `aria-invalid`/`disabled` 用于设置控件的样式。
|
||||
|
||||
```tsx
|
||||
// 无效状态
|
||||
<Field data-invalid>
|
||||
<FieldLabel htmlFor="email">邮箱</FieldLabel>
|
||||
<Input id="email" aria-invalid />
|
||||
<FieldDescription>邮箱地址无效。</FieldDescription>
|
||||
</Field>
|
||||
|
||||
// 禁用状态
|
||||
<Field data-disabled>
|
||||
<FieldLabel htmlFor="email">邮箱</FieldLabel>
|
||||
<Input id="email" disabled />
|
||||
</Field>
|
||||
```
|
||||
|
||||
适用于所有控件:`Input`、`Textarea`、`Select`、`Checkbox`、`RadioGroupItem`、`Switch`、`Slider`、`NativeSelect`、`InputOTP`。
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# 图标
|
||||
|
||||
**始终使用项目配置的 `iconLibrary` 进行导入。** 从项目上下文中查看 `iconLibrary` 字段:`lucide` → `lucide-react`,`tabler` → `@tabler/icons-react`,以此类推。切勿默认使用 `lucide-react`。
|
||||
|
||||
---
|
||||
|
||||
## 按钮中的图标使用 data-icon 属性
|
||||
|
||||
在图标上添加 `data-icon="inline-start"`(前缀)或 `data-icon="inline-end"(后缀)。不在图标上添加尺寸类。
|
||||
|
||||
**错误:**
|
||||
|
||||
```tsx
|
||||
<Button>
|
||||
<SearchIcon className="mr-2 size-4" />
|
||||
Search
|
||||
</Button>
|
||||
```
|
||||
|
||||
**正确:**
|
||||
|
||||
```tsx
|
||||
<Button>
|
||||
<SearchIcon data-icon="inline-start"/>
|
||||
Search
|
||||
</Button>
|
||||
|
||||
<Button>
|
||||
Next
|
||||
<ArrowRightIcon data-icon="inline-end"/>
|
||||
</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 组件内部的图标不加尺寸类
|
||||
|
||||
组件通过 CSS 处理图标尺寸。不要在 `Button`、`DropdownMenuItem`、`Alert`、`Sidebar*` 或其他 shadcn 组件内部的图标上添加 `size-4`、`w-4 h-4` 或其他尺寸类。除非用户明确要求自定义图标尺寸。
|
||||
|
||||
**错误:**
|
||||
|
||||
```tsx
|
||||
<Button>
|
||||
<SearchIcon className="size-4" data-icon="inline-start" />
|
||||
Search
|
||||
</Button>
|
||||
|
||||
<DropdownMenuItem>
|
||||
<SettingsIcon className="mr-2 size-4" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
```
|
||||
|
||||
**正确:**
|
||||
|
||||
```tsx
|
||||
<Button>
|
||||
<SearchIcon data-icon="inline-start" />
|
||||
Search
|
||||
</Button>
|
||||
|
||||
<DropdownMenuItem>
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 将图标作为组件对象传递,而非字符串键
|
||||
|
||||
使用 `icon={CheckIcon}`,而不是使用指向查找映射表的字符串键。
|
||||
|
||||
**错误:**
|
||||
|
||||
```tsx
|
||||
const iconMap = {
|
||||
check: CheckIcon,
|
||||
alert: AlertIcon,
|
||||
}
|
||||
|
||||
function StatusBadge({ icon }: { icon: string }) {
|
||||
const Icon = iconMap[icon]
|
||||
return <Icon />
|
||||
}
|
||||
|
||||
<StatusBadge icon="check" />
|
||||
```
|
||||
|
||||
**正确:**
|
||||
|
||||
```tsx
|
||||
// 从项目配置的 iconLibrary 导入(例如 lucide-react、@tabler/icons-react)。
|
||||
import { CheckIcon } from "lucide-react"
|
||||
|
||||
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
|
||||
return <Icon />
|
||||
}
|
||||
|
||||
<StatusBadge icon={CheckIcon} />
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
# 样式与自定义
|
||||
|
||||
关于主题、CSS 变量以及添加自定义颜色,请参阅 [customization.md](../customization.md)。
|
||||
|
||||
## 目录
|
||||
|
||||
- 语义化颜色
|
||||
- 优先使用内置变体
|
||||
- className 仅用于布局
|
||||
- 避免 space-x-* / space-y-*
|
||||
- 宽高相等时优先使用 size-*
|
||||
- 优先使用 truncate 简写
|
||||
- 不要手动覆写 dark: 颜色
|
||||
- 使用 cn() 处理条件类名
|
||||
- 浮层组件上不要手动设置 z-index
|
||||
|
||||
---
|
||||
|
||||
## 语义化颜色
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<div className="bg-blue-500 text-white">
|
||||
<p className="text-gray-600">辅助文本</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
<div className="bg-primary text-primary-foreground">
|
||||
<p className="text-muted-foreground">辅助文本</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态/状态指示器不要使用原始颜色值
|
||||
|
||||
对于正向、负向或状态指示器,应使用 Badge 变体、`text-destructive` 等语义化标记,或定义自定义 CSS 变量——不要直接使用 Tailwind 原始颜色。
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<span className="text-emerald-600">+20.1%</span>
|
||||
<span className="text-green-500">活跃</span>
|
||||
<span className="text-red-600">-3.2%</span>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
<Badge variant="secondary">+20.1%</Badge>
|
||||
<Badge>活跃</Badge>
|
||||
<span className="text-destructive">-3.2%</span>
|
||||
```
|
||||
|
||||
如果需要的成功/正向颜色不存在对应的语义化标记,请使用 Badge 变体,或请用户向主题中添加自定义 CSS 变量(参见 [customization.md](../customization.md))。
|
||||
|
||||
---
|
||||
|
||||
## 优先使用内置变体
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<Button className="border border-input bg-transparent hover:bg-accent">
|
||||
点击我
|
||||
</Button>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
<Button variant="outline">点击我</Button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## className 仅用于布局
|
||||
|
||||
使用 `className` 处理布局(例如 `max-w-md`、`mx-auto`、`mt-4`),**不要**用它覆写组件的颜色或排版。如需修改颜色,请使用语义化标记、内置变体或 CSS 变量。
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<Card className="bg-blue-100 text-blue-900 font-bold">
|
||||
<CardContent>仪表盘</CardContent>
|
||||
</Card>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
<Card className="max-w-md mx-auto">
|
||||
<CardContent>仪表盘</CardContent>
|
||||
</Card>
|
||||
```
|
||||
|
||||
若要自定义组件的外观,请按以下顺序优先选择:
|
||||
1. **内置变体**——`variant="outline"`、`variant="destructive"` 等。
|
||||
2. **语义化颜色标记**——`bg-primary`、`text-muted-foreground`。
|
||||
3. **CSS 变量**——在全局 CSS 文件中定义自定义颜色(参见 [customization.md](../customization.md))。
|
||||
|
||||
---
|
||||
|
||||
## 避免 space-x-* / space-y-*
|
||||
|
||||
请改用 `gap-*`。`space-y-4` → `flex flex-col gap-4`。`space-x-2` → `flex gap-2`。
|
||||
|
||||
```tsx
|
||||
<div className="flex flex-col gap-4">
|
||||
<Input />
|
||||
<Input />
|
||||
<Button>提交</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 宽高相等时优先使用 size-*
|
||||
|
||||
使用 `size-10` 而非 `w-10 h-10`。适用于图标、头像、骨架屏等场景。
|
||||
|
||||
---
|
||||
|
||||
## 优先使用 truncate 简写
|
||||
|
||||
使用 `truncate` 而非 `overflow-hidden text-ellipsis whitespace-nowrap`。
|
||||
|
||||
---
|
||||
|
||||
## 不要手动覆写 dark: 颜色
|
||||
|
||||
使用语义化标记——它们通过 CSS 变量处理亮色/暗色主题。使用 `bg-background text-foreground` 而非 `bg-white dark:bg-gray-950`。
|
||||
|
||||
---
|
||||
|
||||
## 使用 cn() 处理条件类名
|
||||
|
||||
使用项目中的 `cn()` 工具函数处理条件性或合并的类名字符串。不要在 className 字符串中手动编写三元表达式。
|
||||
|
||||
**错误写法:**
|
||||
|
||||
```tsx
|
||||
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
|
||||
```
|
||||
|
||||
**正确写法:**
|
||||
|
||||
```tsx
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 浮层组件上不要手动设置 z-index
|
||||
|
||||
`Dialog`、`Sheet`、`Drawer`、`AlertDialog`、`DropdownMenu`、`Popover`、`Tooltip`、`HoverCard` 会自行处理层叠顺序。切勿添加 `z-50` 或 `z-[999]`。
|
||||
Reference in New Issue
Block a user