| README.md | ||
Frontend Design System v3.1
适用于所有前端项目的统一视觉与实现规范。
技术栈:React + TypeScript + shadcn/ui + Tailwind CSS + Vite / Next.js
最后更新:2026-07-17
设计哲学:亮色优先 · 信息密集 · 操作克制 · 胶水代码优先
〇、最高红线原则(Agent 必须遵守)
- 禁止手搓基础交互组件。任何按钮、输入框、下拉、表格、弹窗、侧边栏、分页、开关、日期选择等,必须使用 shadcn/ui 组件(或本规范明确批准的扩展)。
- 只写胶水代码:组合已有组件 + 传 props + 处理事件 + 对接业务逻辑。禁止从零实现交互行为。
- 所有尺寸、颜色、圆角、字重、间距必须来自本规范定义的 Token,禁止魔法数字。
- 亮色为默认主题,必须同时支持暗色,通过
class="dark"切换。 - 高度统一:所有中号表单控件(Button、Input、Select、Trigger)默认高度必须为 36px(
h-9)。 - 文字溢出必须处理:标题、表格单元格、Badge 默认
truncate。 - 移动端可点击区域 ≥ 44×44px。
- 违反以上任一条,视为未遵循设计系统。
一、设计哲学
亮色优先、信息密集、操作克制、低视觉噪音。
- 目标用户:运维 / 开发 / 业务人员,长时间盯屏,需要快速扫读关键信息。
- 灵感来源:Linear、Vercel Dashboard、Railway、shadcn/ui 官方示例。
- 原则:
- 用颜色传递状态,不用装饰分散注意力。
- 卡片默认无阴影,仅 hover 时出现轻微强调。
- 圆角、间距、字重严格遵循 8px 网格与本规范阶梯。
- 优先复用,绝不重复造轮子。
二、技术栈(强制)
| 层级 | 选型 | 说明 |
|---|---|---|
| 框架 | React 18/19 + TypeScript | 函数组件 + Hooks |
| 构建 | Vite 或 Next.js (App Router) | 新项目优先 Vite(纯前端)或 Next.js(需 SSR) |
| UI 组件 | shadcn/ui | 复制进项目的源码,禁止再封装黑盒 |
| 样式 | Tailwind CSS v4 + CSS Variables | 所有样式通过 Tailwind 工具类 + 变量 |
| 图标 | lucide-react | 统一使用,禁止 emoji、禁止其他图标库 |
| 状态 | Zustand 或 React Context(按需) | 简单项目可用 useState |
| 路由 | React Router 或 Next.js 内置 | — |
| 表单 | react-hook-form + zod + shadcn Form | 强制 |
| 表格 | shadcn Data Table(基于 TanStack Table) | 强制 |
| 主题 | next-themes 或自实现 class 切换 | 支持 system / light / dark |
安装与初始化要求
# 使用官方 CLI 初始化(必须)
npx shadcn@latest init
# 常用组件按需添加(禁止自己写)
npx shadcn@latest add button input select dropdown-menu dialog sheet card table form badge avatar separator tabs tooltip popover command checkbox switch radio-group label textarea calendar popover date-picker sonner
所有 shadcn 组件必须放在 src/components/ui/,业务组合组件放在 src/components/common/ 或 src/components/layout/。
三、CSS 变量与颜色系统
所有颜色通过 CSS 变量定义,放在 src/index.css(或 globals.css)中。
shadcn 默认使用 HSL,本规范保持兼容并扩展语义色。
3.1 亮色主题(默认)
:root {
--background: 210 40% 98%; /* #f8fafc */
--foreground: 222 47% 11%; /* #0f172a */
--card: 0 0% 100%; /* #ffffff */
--card-foreground: 222 47% 11%;
--popover: 0 0% 100%;
--popover-foreground: 222 47% 11%;
--primary: 217 91% 60%; /* #3b82f6 */
--primary-foreground: 0 0% 100%;
--secondary: 210 40% 96%; /* #f1f5f9 */
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%; /* #475569 */
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%; /* #dc2626 */
--destructive-foreground: 0 0% 100%;
--border: 214 32% 91%; /* #e2e8f0 */
--input: 214 32% 91%;
--ring: 217 91% 60%;
--radius: 0.5rem; /* 8px 基础 */
/* 扩展语义色 */
--success: 142 76% 36%; /* #16a34a */
--success-foreground: 0 0% 100%;
--warning: 32 95% 44%; /* #d97706 */
--warning-foreground: 0 0% 100%;
--info: 199 89% 48%; /* #0891b2 */
--purple: 271 81% 56%; /* #9333ea */
/* 背景层级 */
--bg: 210 40% 98%;
--bg-2: 210 40% 96%;
--bg-3: 214 32% 91%;
/* 文字层级 */
--text: 222 47% 11%;
--text-2: 215 16% 47%;
--text-3: 215 20% 65%; /* #94a3b8 */
/* Header */
--header-bg: 0 0% 100% / 0.88;
}
3.2 暗色主题
.dark {
--background: 224 28% 7%; /* #0f1117 */
--foreground: 210 20% 92%; /* #e4e6ed */
--card: 224 22% 12%; /* #1e2230 */
--card-foreground: 210 20% 92%;
--popover: 224 22% 12%;
--popover-foreground: 210 20% 92%;
--primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 224 20% 16%;
--secondary-foreground: 210 20% 92%;
--muted: 224 20% 16%;
--muted-foreground: 215 14% 55%; /* #8b8fa3 */
--accent: 224 20% 16%;
--accent-foreground: 210 20% 92%;
--destructive: 0 72% 51%;
--destructive-foreground: 0 0% 100%;
--border: 224 16% 20%; /* #2a2e3d */
--input: 224 16% 20%;
--ring: 217 91% 60%;
--success: 142 71% 45%;
--warning: 38 92% 50%;
--info: 189 94% 43%;
--purple: 270 91% 65%;
--bg: 224 28% 7%;
--bg-2: 224 22% 12%;
--bg-3: 224 18% 16%;
--text: 210 20% 92%;
--text-2: 215 14% 55%;
--text-3: 220 10% 40%;
--header-bg: 224 22% 12% / 0.92;
}
3.3 色彩使用规则
| 场景 | 用法 |
|---|---|
| 主操作 / 链接 | bg-primary text-primary-foreground |
| 成功 / 在线 | text-success 或 bg-success/10 text-success |
| 错误 / 危险 | text-destructive 或 bg-destructive/10 text-destructive |
| 警告 | text-warning 或 bg-warning/10 text-warning |
| 次要文字 | text-muted-foreground |
| 半透明标签底 | bg-xxx/10 或 bg-xxx/15(禁止纯色背景) |
| 进度条轨道 | bg-muted |
| 遮罩 | bg-black/60 + backdrop-blur-sm |
3.4 数据可视化(图表)配色与样式
当项目使用图表库(Recharts、Chart.js、ECharts、Tremor 等)时,必须遵守以下规范。
3.4.1 分类色板(按顺序使用)
export const chartColors = [
"hsl(217 91% 60%)", // primary 蓝
"hsl(142 76% 36%)", // success 绿
"hsl(32 95% 44%)", // warning 橙
"hsl(271 81% 56%)", // purple
"hsl(199 89% 48%)", // cyan / info
"hsl(0 84% 60%)", // destructive 红
"hsl(215 16% 47%)", // muted
]
暗色模式下使用同一组 HSL,亮度已在语义色中适配。
3.4.2 强制样式
| 元素 | 规范 |
|---|---|
| 背景 | 透明或 hsl(var(--card)) |
| 网格线 | hsl(var(--border)),线宽 1px,虚线可选 |
| 坐标轴文字 | text-[11px] fill-muted-foreground |
| Tooltip | 使用 shadcn 风格:bg-popover border border-border rounded-lg shadow-md text-[12px] |
| 图例 | 字号 12px,颜色 text-muted-foreground |
| 线宽(折线) | 2px |
| 点半径 | 3–4px |
| 动画 | 可开启,时长 ≤ 500ms |
3.4.3 禁止
- 禁止使用图表库默认的彩虹色
- 禁止硬编码
#3b82f6等,必须引用上述色板或 CSS 变量 - 禁止在暗色模式下仍使用浅色网格导致看不清
四、字体系统
4.1 字体栈
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
/* 等宽 */
font-family: "SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace;
在 tailwind.config 中:
fontFamily: {
sans: ['-apple-system', 'BlinkMacSystemFont', '"SF Pro Display"', '"Segoe UI"', 'Roboto', 'sans-serif'],
mono: ['"SF Mono"', 'Menlo', 'Consolas', 'monospace'],
}
4.2 字号与字重阶梯(强制)
| 用途 | 类名 / 尺寸 | 字重 | 其他 |
|---|---|---|---|
| Logo / 页面大标题 | text-base font-bold (16px) |
700 | whitespace-nowrap |
| 卡片大数值 | text-xl font-bold tracking-tight (20px) |
700 | letter-spacing: -0.5px |
| 卡片标题 / Section 标题 | text-[15px] font-bold |
700 | — |
| 正文 / 按钮文字 | text-[13px] font-medium |
500/600 | 主要正文尺寸 |
| 次要信息 / Header 信息 | text-xs font-semibold (12px) |
600 | — |
| Section 小标题 | text-xs font-bold uppercase tracking-wider |
700 | letter-spacing: 1px |
| Badge / 状态标签 | text-[11px] font-bold uppercase tracking-wide |
700 | — |
| 极小标注 | text-[10px] font-bold uppercase |
700 | — |
| 代码 / 日志 | text-xs font-mono |
400 | leading-relaxed |
字重规则:
- 700:所有标题、数值、Badge、主按钮文字
- 600:次要按钮、Header 信息
- 500:正文描述
- 400:仅长文本与代码块
4.3 行高
- 正文:
leading-normal或leading-relaxed(1.5) - 标题 / 数值:
leading-none或leading-tight - 表格单元格:
leading-none
4.4 移动端
@media (max-width: 768px) {
/* 正文不小于 14px */
body { font-size: 14px; }
/* input 防 iOS 缩放 */
input, select, textarea { font-size: 16px !important; }
}
五、圆角系统
| 元素 | 圆角 | Tailwind |
|---|---|---|
| 按钮 / 输入框 / Select | 8px | rounded-md(对应 --radius) |
| 卡片 | 14px | rounded-xl |
| 大容器 / Section | 16px | rounded-2xl |
| Dialog / Sheet | 18px | rounded-[18px] |
| Badge / 胶囊按钮 | 9999px | rounded-full |
| 代码块 / 日志区 | 12px | rounded-lg |
| 进度条 | 3px | rounded-[3px] |
| 状态圆点 | 50% | rounded-full |
| Logo 色块 | 7px | rounded-[7px] |
规则:圆角随元素尺寸正相关。控制类组件统一 rounded-md。
六、间距系统(8px 基准)
| 场景 | 间距 | Tailwind |
|---|---|---|
| 页面左右内边距 | 24px | px-6 |
| Header 高度 | 52px | h-[52px] |
| Section 垂直间距 | 24px | space-y-6 / gap-6 |
| Grid 卡片间距 | 14px | gap-3.5 |
| 卡片内边距 | 16–18px | p-4 或 px-[18px] py-4 |
| 表单行间距 | 16px | space-y-4 |
| 按钮组间距 | 8px | gap-2 |
| 图标与文字间距 | 6–8px | gap-1.5 / gap-2 |
| Section 标题下方 | 12px | mb-3 |
| Dialog 内边距 | 28px | p-7 |
禁止使用非 4 的倍数间距(除 14px 卡片 gap 外)。
七、阴影、边框与层级
7.1 阴影与边框
- 边框:统一
border border-border(1px solid) - 卡片默认无阴影
- 卡片 hover:
className="... transition-all hover:shadow-[0_0_0_1px_hsl(var(--primary)),0_6px_24px_rgba(59,130,246,0.1)]" - 浮层(Dropdown / Popover):
shadow-md - Dialog:
shadow-lg - Header:无阴影,使用
border-b border-border
7.2 Z-Index 层级表(强制)
所有项目必须使用以下层级,禁止随意写 z-50、z-9999 等魔法数字。
| 层级 | z-index | 用途 |
|---|---|---|
| 基础内容 | auto / 0 |
页面正常流 |
| 粘性表头 / 局部悬浮 | 10 |
表格 sticky header |
| Header | 50 |
顶部导航 |
| Sidebar(展开遮罩) | 40 |
移动端侧边栏遮罩 |
| Dropdown / Popover / Select | 60 |
下拉与气泡 |
| Tooltip | 70 |
提示 |
| Dialog / Sheet 遮罩 | 80 |
模态遮罩 |
| Dialog / Sheet 内容 | 90 |
模态内容 |
| Toast | 100 |
消息提示(最高) |
在 Tailwind 中推荐扩展:
// tailwind preset
zIndex: {
sticky: "10",
header: "50",
dropdown: "60",
tooltip: "70",
modal: "80",
modalContent: "90",
toast: "100",
}
使用时写 z-header、z-dropdown 等,禁止硬编码数字。
八、图标系统
8.1 强制规则
- 只使用
lucide-react - 禁止 emoji、禁止 FontAwesome、禁止其他图标库
- 统一
strokeWidth={1.8}(或 2) - 尺寸通过 class 控制
8.2 尺寸
| 场景 | 尺寸 | 类名 |
|---|---|---|
| Section 标题旁 | 16×16 | h-4 w-4 |
| Header / 卡片内 | 14×14 | h-3.5 w-3.5 |
| 按钮内 / 极小 | 12×12 | h-3 w-3 |
| 大图标(空状态等) | 24×24 | h-6 w-6 |
8.3 颜色
- 默认:
text-muted-foreground - 可交互:
text-foreground - 强调:跟随父级
text-primary/text-destructive等
8.4 常用图标映射(必须统一)
| 用途 | 图标名 |
|---|---|
| 设置 | Settings |
| 搜索 | Search |
| 刷新 | RefreshCw |
| 关闭 | X |
| 成功 | Check / CheckCircle2 |
| 警告 | AlertTriangle |
| 错误 | XCircle |
| 信息 | Info |
| 时间 | Clock |
| 终端 / 日志 | Terminal |
| 复制 | Copy |
| 亮色 | Sun |
| 暗色 | Moon |
| 菜单 | Menu |
| 服务器 | Server |
| CPU | Cpu |
| 存储 | HardDrive |
| 用户 | User |
| 退出 | LogOut |
| 加号 | Plus |
| 更多 | MoreHorizontal |
| 编辑 | Pencil |
| 删除 | Trash2 |
| 外部链接 | ExternalLink |
| 侧边栏折叠 | PanelLeftClose / PanelLeftOpen |
九、组件规范(核心,Agent 必须严格遵守)
9.1 按钮(Button)
必须使用 import { Button } from "@/components/ui/button"
变体与用途
| 变体 | 用途 | 类名补充 |
|---|---|---|
default |
主操作 | — |
secondary |
次要操作 | — |
outline |
边框按钮 | — |
ghost |
文字/图标按钮、取消 | — |
destructive |
删除 / 危险 | — |
link |
链接样式 | — |
尺寸(强制)
| size | 高度 | 类名 | 使用场景 |
|---|---|---|---|
sm |
28px (h-7) |
size="sm" |
表格内操作、紧凑区 |
| 默认 | 36px (h-9) |
不写或 size="default" |
几乎所有场景 |
lg |
40px (h-10) |
size="lg" |
页面级主 CTA |
icon |
36×36 | size="icon" |
纯图标按钮 |
强制样式覆盖(在 button.tsx 中统一)
// 默认高度必须是 h-9 (36px)
default: "h-9 px-4 py-2",
sm: "h-7 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
- 字重:
font-semibold(600) - 圆角:
rounded-md - 图标与文字间距:
gap-1.5或gap-2 - active 态:轻微
scale-[0.97](可选,在全局加)
禁止
- 禁止自定义
height/h-[xxpx] - 禁止使用原生
<button>而不包 Button 组件 - 禁止主按钮使用非 primary 色
9.2 输入框(Input)
必须使用 import { Input } from "@/components/ui/input"
- 高度:强制
h-9(36px) - 圆角:
rounded-md - 字号:
text-sm(13–14px) - 边框:
border-input - focus:
ring-2 ring-ring ring-offset-2 - 禁止高度与 Button 不一致
9.3 选择器(Select)
必须使用 shadcn Select 系列组件。
- Trigger 高度:强制
h-9 - 与 Input、Button 完全对齐
- 禁止自己写下拉菜单
9.4 卡片(Card)
必须使用 import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "@/components/ui/card"
<Card className="rounded-xl border bg-card shadow-none transition-all hover:shadow-[0_0_0_1px_hsl(var(--primary)),0_6px_24px_rgba(59,130,246,0.1)]">
<CardHeader className="p-4 pb-2">
<CardTitle className="text-[15px] font-bold">标题</CardTitle>
</CardHeader>
<CardContent className="p-4 pt-0">
...
</CardContent>
</Card>
- 默认无阴影
- 内边距 16–18px
- 圆角
rounded-xl(14px)
9.5 表格(Data Table)
必须使用 shadcn Data Table(基于 @tanstack/react-table)。
- 表头:
text-xs font-semibold uppercase tracking-wider text-muted-foreground - 行高:最小 44px(方便点击)
- 单元格文字默认
truncate - 操作列固定右侧
- 移动端(<640px):表格容器加
overflow-x-auto
禁止手写 <table> 实现业务表格。
9.6 Badge / 状态标签
import { Badge } from "@/components/ui/badge"
// 语义变体
<Badge variant="default">默认</Badge>
<Badge className="bg-success/10 text-success border-transparent">成功</Badge>
<Badge className="bg-destructive/10 text-destructive border-transparent">错误</Badge>
<Badge className="bg-warning/10 text-warning border-transparent">警告</Badge>
- 圆角:
rounded-full - 字号:
text-[11px] font-bold uppercase - 内边距:
px-2.5 py-0.5
9.7 状态圆点
<span className="inline-block h-1.5 w-1.5 rounded-full bg-success" /> // 在线
<span className="inline-block h-1.5 w-1.5 rounded-full bg-destructive" /> // 离线
<span className="inline-block h-1.5 w-1.5 rounded-full bg-warning" /> // 警告
<span className="inline-block h-1.5 w-1.5 rounded-full bg-muted-foreground" /> // 未知
9.8 Dialog / Sheet(弹窗 / 抽屉)
- Dialog:居中,宽度默认
sm:max-w-md,圆角rounded-[18px],内边距p-7 - Sheet:侧滑,常用于移动端或详情
- 遮罩:
bg-black/60 backdrop-blur-sm - 禁止自己实现 Modal
9.9 Header(顶部导航)
高度:52px
定位:sticky top-0 z-50
背景:bg-background/88 backdrop-blur-xl border-b
布局:flex items-center justify-between px-6
左侧:Logo(26×26 圆角色块 + 文字) + 状态点
右侧:操作按钮组 + 主题切换
主题切换按钮使用 Button variant="ghost" size="icon" + Sun / Moon 图标。
9.10 Sidebar(侧边栏)
- 宽度:展开 240px,折叠 64px
- 背景:
bg-card或bg-background - 边框:
border-r border-border - 菜单项高度:36–40px
- 激活态:
bg-accent text-accent-foreground或左侧 2px primary 指示条 - 图标 + 文字,折叠时只显示图标 + Tooltip
- 必须使用可折叠实现(可基于 shadcn 社区 Sidebar 或自组合 Sheet + 导航)
9.11 表单
强制使用:
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"
- Label 与控件间距:
space-y-2 - 错误提示:红色文字,位于控件下方
- 桌面端 label 可固定宽度右对齐(可选)
9.12 其他强制组件
| 需求 | 必须使用 |
|---|---|
| 开关 | Switch |
| 复选 | Checkbox |
| 单选 | RadioGroup |
| 分页 | 自组合或社区 Pagination(基于 shadcn) |
| 标签页 | Tabs |
| 提示 | Tooltip |
| 命令面板 | Command |
| 日期 | Calendar + Popover |
| 进度 | Progress |
| 骨架 | Skeleton |
| 分隔线 | Separator |
| 头像 | Avatar |
9.13 Toast / 消息提示
9.13.1 强制使用
使用 shadcn 推荐的 sonner 或 @radix-ui/react-toast 封装(通过 npx shadcn add sonner)。
禁止自己写固定定位的 div 作为 Toast。
9.13.2 位置与行为
| 属性 | 值 |
|---|---|
| 位置 | 右下角(bottom-right) |
| 自动关闭 | 成功 / 信息 3s;警告 / 错误 5s |
| 最大同时显示 | 3 条 |
| 动画 | 从下方滑入 + 淡入,200–300ms |
9.13.3 类型与样式
toast.success("操作成功")
toast.error("操作失败,请重试")
toast.warning("请注意…")
toast.info("提示信息")
toast.loading("处理中…") // 可手动 dismiss
- 左侧可保留 3px 语义色竖线(成功绿 / 错误红 / 警告橙)
- 文字:
text-[13px] font-medium - 圆角:
rounded-lg(12px) - 阴影:
shadow-lg
9.13.4 禁止
- 禁止用
alert() - 禁止用 Dialog 模拟轻量提示
- 禁止 Toast 覆盖重要操作按钮
9.14 自定义业务组合组件
shadcn 原生组件之外,以下组合组件必须按本规范实现(或直接使用共享包中的实现),禁止两个项目各自自由发挥。
9.14.1 PageHeader(页面标题区)
// 必须包含:标题 + 可选描述 + 可选右侧操作区
interface PageHeaderProps {
title: string
description?: string
actions?: React.ReactNode
}
// 结构强制
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between mb-6">
<div>
<h1 className="text-base font-bold tracking-tight">{title}</h1>
{description && (
<p className="text-[13px] text-muted-foreground mt-1">{description}</p>
)}
</div>
{actions && <div className="flex items-center gap-2 mt-3 sm:mt-0">{actions}</div>}
</div>
- 标题字号:
text-base font-bold(16px) - 描述:
text-[13px] text-muted-foreground - 与下方内容间距:
mb-6(24px)
9.14.2 StatCard(数据指标卡片)
interface StatCardProps {
label: string // 小标题,uppercase
value: string | number // 大数值
icon?: React.ReactNode
trend?: { value: string; direction: "up" | "down" | "neutral" }
className?: string
}
强制样式:
- 使用
Card,圆角rounded-xl,内边距p-4或px-[18px] py-4 - label:
text-[11px] font-bold uppercase tracking-wider text-muted-foreground - value:
text-xl font-bold tracking-tight - 默认无阴影,hover 使用本规范卡片 hover 效果
- 图标放在右上角,
h-4 w-4 text-muted-foreground
9.14.3 EmptyState(空状态)
interface EmptyStateProps {
icon?: React.ReactNode // 默认使用 Inbox 或 FileX
title: string
description?: string
action?: React.ReactNode // 通常是一个 Button
}
强制布局:
<div className="flex flex-col items-center justify-center py-16 px-6 text-center">
<div className="h-12 w-12 rounded-full bg-muted flex items-center justify-center mb-4">
{icon || <Inbox className="h-6 w-6 text-muted-foreground" />}
</div>
<h3 className="text-[15px] font-bold mb-1">{title}</h3>
{description && (
<p className="text-[13px] text-muted-foreground max-w-sm mb-6">{description}</p>
)}
{action}
</div>
- 禁止只显示一行「暂无数据」文字
- 图标容器必须是圆形 +
bg-muted
9.14.4 PageLoading(整页加载)
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
或使用多个 Skeleton 按页面真实结构排列(推荐仪表盘场景)。
9.14.5 ErrorState(错误 / 失败状态)
interface ErrorStateProps {
title?: string // 默认「加载失败」
description?: string
retry?: () => void
}
- 图标使用
AlertTriangle或XCircle,颜色text-destructive - 提供「重试」按钮(
variant="outline") - 布局与 EmptyState 保持一致的垂直居中结构
9.14.6 面包屑(Breadcrumb)
必须使用 shadcn Breadcrumb 组件。
- 字号:
text-[13px] - 当前页:
font-medium text-foreground - 上级:
text-muted-foreground hover:text-foreground - 分隔符使用
ChevronRight,h-3.5 w-3.5
9.14.7 分页器(Pagination)
必须基于 shadcn 风格实现(或共享包提供)。
- 按钮高度:
h-9(与规范控件一致) - 当前页:
variant="outline"或bg-primary text-primary-foreground - 移动端可只显示「上一页 / 下一页 + 页码信息」
9.14.8 业务组件放置位置
src/components/common/
├── PageHeader.tsx
├── StatCard.tsx
├── EmptyState.tsx
├── ErrorState.tsx
├── PageLoading.tsx
└── ...
两个项目应尽量使用共享包中的同一实现;若暂时无法共享,则必须按本规范像素级一致实现。
9.15 加载态 / 空状态 / 错误态统一模式
| 场景 | 必须使用的模式 | 禁止 |
|---|---|---|
| 首次加载整页 | PageLoading 或结构化 Skeleton |
白屏、无提示 |
| 区块加载 | 对应区域 Skeleton |
整个页面转圈 |
| 列表 / 表格无数据 | EmptyState |
只显示「暂无数据」一行字 |
| 接口报错 | ErrorState + 重试按钮 |
静默失败、只 console.error |
| 按钮提交中 | Button disabled + Loader2 图标旋转 |
可重复点击 |
| 表格刷新 | 表头或右上角小 Spinner,保留旧数据 | 清空表格再转圈 |
Skeleton 颜色必须使用 bg-muted,圆角与真实组件一致。
十、布局系统
10.1 页面结构
<div className="min-h-screen bg-background text-foreground">
<Header />
<div className="flex">
<Sidebar />
<main className="flex-1 p-6 overflow-auto">
{/* 内容 */}
</main>
</div>
</div>
10.2 Grid
// 4 列
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-3.5">
// 2 列
<div className="grid grid-cols-1 md:grid-cols-2 gap-3.5">
断点:
< 640px:1 列640–1280px:2 列≥ 1280px:4 列
10.3 内容最大宽度(可选)
工具类页面可不限制;文档/设置页可加 max-w-5xl mx-auto。
十一、主题切换
必须支持 light / dark / system。
推荐使用 next-themes(Next.js)或自实现:
// 切换时给 <html> 添加/移除 class="dark"
document.documentElement.classList.toggle("dark")
切换按钮放在 Header 右侧,使用 Sun / Moon 图标,Button variant="ghost" size="icon"。
初始化时读取 localStorage 或 prefers-color-scheme。
十二、移动端适配
强制规则
- 所有可点击元素最小 44×44px(
min-h-11 min-w-11) - 正文字号 ≥ 14px
- Input / Select 在移动端
text-base(16px)防止 iOS 缩放 - 底部安全区:
pb-safe或pb-[env(safe-area-inset-bottom)] - 表格横向滚动
- Header 可换行或变成抽屉菜单
- Sheet 优先于 Dialog(移动端)
断点参考
sm: 640px
md: 768px
lg: 1024px
xl: 1280px
十三、动画与过渡
| 元素 | 属性 | 时长 |
|---|---|---|
| 按钮 hover/active | all | 150ms |
| 卡片 hover | all | 200–250ms |
| Dialog / Sheet | data-state 动画 | 200–300ms |
| 页面切换 | — | 避免过度动画 |
原则:短、克制、即时反馈。禁止花哨动画。
十四、文字溢出处理(强制)
// 单行截断
className="truncate"
// 多行截断(2 行)
className="line-clamp-2"
// 数字 / Badge / 状态永不换行
className="whitespace-nowrap"
- 卡片标题、表格单元格、Header 信息、侧边栏菜单文字:默认
truncate - 长代码 / 日志:
break-all+ 横向滚动
十五、项目结构(推荐)
src/
├── components/
│ ├── ui/ # shadcn 原始组件(只通过 CLI 添加)
│ ├── common/ # 业务通用组合组件(BadgeStatus、PageHeader 等)
│ └── layout/ # Header、Sidebar、AppShell
├── hooks/ # 自定义 hooks
├── lib/ # utils.ts(cn 函数等)
├── styles/
│ └── globals.css # CSS 变量 + Tailwind 指令
├── pages/ 或 app/ # 路由页面
├── stores/ # Zustand
└── types/
十六、Agent 开发 Checklist(每页必须自检)
16.1 核心检查
- 是否全部使用 shadcn/ui 组件?有无手搓 Button/Input/Select/Table/Modal?
- 所有中号控件高度是否为 36px(h-9)?
- 按钮与输入框并排时高度是否完全一致?
- 颜色是否全部来自 CSS 变量 / Tailwind token?
- 标题、表格单元格是否处理了 truncate?
- 暗色模式是否正常?
- 移动端可点击区域是否 ≥ 44px?
- Input 在移动端是否 ≥ 16px?
- 图标是否全部来自 lucide-react?
- 是否存在魔法数字(随意 px 值)?
- 卡片是否默认无阴影,仅 hover 强调?
- 表单是否使用 react-hook-form + zod + shadcn Form?
16.2 扩展检查
- CSS 变量与 Tailwind preset 是否来自共享源(而非本地抄写)?
- PageHeader / StatCard / EmptyState / ErrorState 是否按 §9.14 实现?
- Toast 是否使用 sonner(或统一封装),位置与时长是否符合规范?
- 空数据是否使用 EmptyState 而不是一行文字?
- z-index 是否只使用规范中的层级 token(§7.2)?
components/ui是否与另一项目同源(共享包或同一基准版本)?- 图表颜色是否使用 §3.4 色板?
十七、反模式(明确禁止)
- 自己写
<button>、<input>、<select>、<table>实现业务交互 - 按钮高度写成 32px / 34px / 40px 等非规范值
- 使用 emoji 作为图标
- 卡片默认加 shadow
- 文字溢出不处理直接撑破布局
- 硬编码颜色(
#3b82f6、red等) - 在业务组件里大量写自定义 CSS 覆盖 shadcn 样式
- 移动端忽略触摸目标与安全区
- 不支持暗色模式
- 引入除 lucide-react 外的图标库
十八、版本历史
| 版本 | 日期 | 变更 |
|---|---|---|
| v1.0 | 2026-06-12 | 原 NaiveUI 规范(暗色优先) |
| v2.0 | 2026-07-04 | 亮色优先 + NaiveUI 完善 |
| v3.0 | 2026-07-17 | 全面转向 React + shadcn/ui + Tailwind,强化胶水代码与 AI 可执行细节 |
| v3.1 | 2026-07-17 | 合并补充章节:图表配色、Z-Index、业务组合组件、Toast、状态模式、自检清单;新增物理同步与版本锁定 |
十九、物理同步机制(强制跨项目一致)
两个(或多个)独立项目必须在视觉 Token 与基础 UI 源码层面物理同源,禁止「各自照文档抄写」。
19.1 推荐方案(二选一)
| 方案 | 适用场景 | 说明 |
|---|---|---|
| A. 私有 npm 包(推荐) | 项目已使用 monorepo 或有私有源 | 发布 @yourorg/design-tokens + @yourorg/ui |
| B. Git Submodule / 子仓库 | 快速落地、暂不发 npm | 两个项目共同引用同一 design-system 仓库 |
19.2 必须共享的内容
@yourorg/design-tokens # 或 submodule 中的 packages/tokens
├── src/
│ ├── css-variables.css # 完整 :root 与 .dark 变量(本规范 §3)
│ ├── tailwind-preset.ts # fontFamily、borderRadius、colors 映射
│ └── index.ts # 导出
└── package.json
@yourorg/ui # 可选但强烈推荐
├── src/
│ ├── components/ui/ # 锁定版本的 shadcn 组件源码
│ └── components/common/ # 本规范定义的业务组合组件
└── package.json
19.3 强制规则
- CSS 变量与 Tailwind preset 只允许存在一份源文件,两个项目必须
import同一份,禁止在业务仓库内再维护一份副本。 - 修改 Token 后必须同时发版 / 更新 submodule,并在两个项目中同步升级。
- 业务项目中的
globals.css只允许:@import "@yourorg/design-tokens/css-variables.css"; @tailwind base; @tailwind components; @tailwind utilities; - 禁止在业务项目中覆盖
--primary、--radius、控制高度等核心变量。
二十、shadcn 组件版本锁定与共享
20.1 问题
shadcn 是「复制源码」模式,不同时间执行 npx shadcn add 可能得到不同实现,导致两个项目组件行为或 class 不一致。
20.2 强制措施
-
锁定 components.json 与 CLI 版本
在项目根目录记录:// components.json 中注明 { "aliases": { ... }, "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", // 或选定的 style,两个项目必须相同 "rsc": false, "tsx": true, "tailwind": { ... } } -
推荐把
src/components/ui/纳入共享包(@yourorg/ui)- 新增组件时只在共享包中执行
shadcn add - 业务项目通过包引用,不再各自
npx shadcn add
- 新增组件时只在共享包中执行
-
若暂时无法共享 UI 包
- 建立一个「基准日期」或「基准 commit」
- 两个项目的
components/ui必须从同一 commit 复制 - 后续升级必须同步进行,并记录在 changelog
-
禁止在业务项目中直接修改
components/ui下的源码来「临时修样式」。样式差异必须通过 Tailwind 工具类或共享 Token 解决。
本规范是唯一真相来源。
Agent 在生成任何前端代码前,必须完整阅读并严格遵守以上所有条款。
有疑问时,优先选择「使用已有 shadcn 组件 + 本规范 Token」,而不是自己创造。