Files
model-platform/STYLE_GUIDE.md
T
2026-08-27 17:34:10 +08:00

528 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端样式设计规范 (STYLE_GUIDE)
> **版本**:v2.0 — 规范化版本
>
> **文档性质变更说明**:v1.0 由现有代码反向提炼而来,仅描述"现状",并列出大量"新旧并存 / 待统一"的问题清单。
> **v2.0 起,本文档是唯一权威规范**:所有历史上并存的多套数值(多个品牌蓝、多个灰色、多套圆角体系等)已在本版本中**收敛为唯一官方取值**。
>
> - 🟢 **新代码 / 其他系统 / 新项目**:必须(MUST)按本文档执行,禁止再引入本文档之外的颜色、字号、圆角、间距任意值。
> - 🟡 **存量 legacy 代码**:允许暂时保留(不要求立即批量重构),但**触碰到的每一处**(改动、重构、修 bug)都必须顺手改为符合本规范的取值,禁止在旧代码基础上"抄一份"旧的任意值到新地方。
> - 本指南**不强制使用特定 UI 组件框架**React/Vue/Tailwind/Ant Design 等均可),只强制**最终视觉呈现**(色值、字号、圆角、阴影、间距、过渡时长/缓动)与本文档一致。不同技术栈的团队,请把本文档中的 CSS 变量按第 12 节的方法映射到自己的 token/主题系统。
---
## 0. 规范优先级说明
文档中出现的关键词含义(参考 RFC 2119 风格):
| 关键词 | 含义 |
|---|---|
| **必须 / 禁止** | 强制性规则,新代码不得违反,Code Review 应作为拒绝合并的理由 |
| **应当** | 强烈建议,没有特殊原因必须遵守;有正当理由例外时需在 PR 中说明 |
| **可以** | 允许的做法,非强制 |
---
## 1. 设计概览
项目整体风格是 **浅色 + 深蓝侧栏** 的 SaaS 控制台风格,所有新页面 / 新模块必须延续此基调:
- **基调色**:极浅蓝灰背景 `#f3f6f9`,纯白卡片 `#ffffff`,深蓝侧栏 `#09233f`
- **品牌主色**:饱和中蓝 `#1978d4`(唯一官方品牌蓝,见 §2.7 废弃旧值列表)。
- **状态色**:成功绿 `#18b979` / 危险红 `#d75b5b`(唯一官方危险色)/ 警告橙 `#e6a33c`
- **形状语言**:统一采用 `rounded-2xl / 3xl / 4xl` 圆角阶梯(4 / 6 / 8px 递增,见 §4.3),**禁止**新代码再使用任意像素圆角(如 `rounded-[7px]`)。
- **字体**Inter Variable`/fonts/Inter-Variable.ttf`weight 100900+ PingFang SC / Microsoft YaHei / system-ui 中文回落。
- **阴影**`shadow-md`(卡片)/ `shadow-xl`(对话框/抽屉),禁止新增自定义阴影任意值。
- **动效**:统一 100–200ms 过渡(抽屉类组件可到 450ms),弹层统一用 `data-open`/`data-closed` 进出模式。
---
## 2. 色彩系统(唯一权威取值)
> **规则**:任何新代码中出现的颜色,必须来自本节表格中的具名 token 或对应 CSS 变量 / Tailwind 语义类。**禁止**在新代码里直接写裸 hex 值,除非本节明确未覆盖的一次性边缘场景(且需在 PR 中说明原因)。
### 2.1 Token 定义位置
- 品牌 token`frontend/app/app.css` `@theme { ... }` 块。
- 侧栏 token`frontend/app/app.css` `@theme inline` + `:root { --sidebar-... }`
- shadcn 语义 token`bg-primary` / `text-foreground` 等)实际值由 `@theme` 覆盖,UI 组件一律使用语义 token,不写 hex。
### 2.2 品牌色(Brand / Accent
| 名称 | 十六进制 | CSS 变量 | 用途 |
|---|---|---|---|
| brand | `#1978d4` | `--color-brand` | 主色:CTA、链接、选中态、强调(对应 `bg-primary`/`text-primary` |
| brand-strong | `#0e5fb9` | `--color-brand-strong` | 主色按下/激活 |
| brand-soft | `#edf6ff` | `--color-brand-soft` | 主色浅底:标签背景、提示行 |
| **brand-deep**(新增) | `#0e5bad` | `--color-brand-deep` | 渐变按钮的深色结束端(替代历史上散落的 `#1268c9`/`#1175d8`/`#0e5bad` 等值) |
**渐变按钮官方写法**(如仍需渐变视觉的按钮):`linear-gradient(135deg, var(--color-brand), var(--color-brand-deep))`。**禁止**再手写新的渐变端点色值。
### 2.3 状态色
| 名称 | 十六进制 | CSS 变量 | 用途 |
|---|---|---|---|
| success / success-strong / success-soft | `#18b979` / `#0f7d55` / `#e8f7f1` | `--color-success*` | 成功提示、确认按钮 |
| danger / danger-strong / danger-soft | `#d75b5b` / `#a94f4f` / `#fff6f6` | `--color-danger*` | 错误、删除、危险操作(对应 `bg-destructive`|
| warning / warning-soft | `#e6a33c` / `#fff0ee` | `--color-warning*` | 警示提示 |
### 2.4 中性文本 / 边框 / 背景
| 名称 | 十六进制 | CSS 变量 | 用途 |
|---|---|---|---|
| ink | `#27394d` | `--color-ink` | 主要正文 |
| ink-muted | `#5d7186` | `--color-ink-muted` | 次要正文 |
| ink-subtle | `#8b99a8` | `--color-ink-subtle` | 辅助说明、占位 |
| **ink-caption**(新增) | `#748598` | `--color-ink-caption` | 表格表头/卡片小标签等"更淡一档"的说明文字(替代历史上 `#748598`/`#758497`/`#8796a6`/`#8a9aaa` 等近似灰色) |
| line | `#dce4ec` | `--color-line` | 标准边框(对应 `border-border`),替代历史上 `#dce5ed`/`#dfe7ef`/`#dfe6ed`/`#e7ecf1`/`#e8edf2`/`#e0e7ee` 等近似边框色 |
| line-soft | `#edf1f5` | `--color-line-soft` | 表格行/分隔线 |
| bg | `#f3f6f9` | `--color-bg` | 页面背景 |
| bg-panel | `#ffffff` | `--color-bg-panel` | 卡片/面板背景 |
| bg-canvas | `#f9fbfd` | `--color-bg-canvas` | 画布背景 |
| bg-log | `#17212b` | `--color-bg-log` | 日志/代码块深色背景 |
### 2.5 侧栏 token(独立主题)
| 名称 | 值 | 用途 |
|---|---|---|
| `--sidebar-background` | `#09233f` | 深蓝侧栏底 |
| `--sidebar-foreground` | `#c9d7e7` | 侧栏默认文字 |
| `--sidebar-primary` | `#1479e8` | 侧栏内强调主色 |
| `--sidebar-primary-foreground` | `#ffffff` | 主色文字 |
| `--sidebar-accent` | `rgba(255,255,255,0.06)` | 选中态背景 |
| `--sidebar-accent-foreground` | `#ffffff` | 选中态文字 |
| `--sidebar-border` | `rgba(255,255,255,0.08)` | 侧栏内分隔线 |
| `--sidebar-ring` | `#5ca9ff` | focus ring |
**规则**:任何侧栏相关的自定义样式(如背景渐变)必须引用 `var(--sidebar-background)` 等变量,**禁止**再硬编码 `#09233f`
### 2.6 shadcn 语义 token 映射
| Tailwind 类 | 对应变量 | 用途 |
|---|---|---|
| `bg-primary` / `text-primary` / `border-primary` | `--color-brand` | 主按钮、链接 |
| `bg-secondary` | 浅灰 | 次级按钮 / 输入框默认态 |
| `bg-destructive` / `text-destructive` | `--color-danger` | 危险按钮 |
| `bg-muted` / `text-muted-foreground` | 浅灰底 + `--color-ink-muted` | 次要容器、辅助文字 |
| `bg-popover` / `text-popover-foreground` | 白底 | 浮层、菜单、对话框 |
| `bg-background` / `text-foreground` | 页面背景 + `--color-ink` | body、文本默认 |
| `bg-card` / `text-card-foreground` | 卡片 | Card 容器 |
| `bg-input` / `border-input` | 浅灰 | 输入框背景 |
| `border-border` | `--color-line` | 通用边框 |
| `border-ring` / `ring-ring` | 浅蓝 | focus 边框 / 焦点环 |
| `border-destructive` | `--color-danger` | 错误状态边框 |
### 2.7 已废弃色值(禁止新代码使用)
以下 hex 曾在 legacy 页面中散落使用,**新代码严禁再出现**,一律替换为 §2.2–2.4 对应 token
| 废弃值 | 替换为 |
|---|---|
| `#1881e7` / `#126ac3` / `#1268c9` / `#1175d8` / `#0e5bad` / `#1479e8`(页面内联渐变)/ `#2279c7` | `--color-brand` / `--color-brand-strong` / `--color-brand-deep`(渐变见 §2.2 |
| `#e74c3c` | `--color-danger``#d75b5b` |
| `#748598` / `#758497` / `#8796a6` / `#8a9aaa` | `--color-ink-caption`(新代码首选)或 `--color-ink-muted` / `--color-ink-subtle`(视层级而定) |
| `#dce5ed` / `#dfe7ef` / `#dfe6ed` / `#e7ecf1` / `#e8edf2` / `#e0e7ee` | `--color-line` |
| `#81b2e7` | `--color-brand-soft` 场景下配合使用,或改用 `--color-brand` 的透明度变体(如 `text-primary/70`),不再单独定义新 hex |
---
## 3. 字体系统
### 3.1 字体族
```css
--font-sans: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
```
字体文件:`public/fonts/Inter-Variable.ttf`(含 italic 变体),`font-display: swap`
### 3.2 字号阶梯(唯一官方取值)
**规则**:新代码**必须**从下表取值,**禁止**再使用未定义的任意 `text-[Npx]`
| Token / 类 | 像素 | 用途 |
|---|---|---|
| `text-4xs`(新增 token | 8px | badge / 角标(原 `text-[8px]` |
| `text-3xs`(新增 token) | 9px | 极小辅助文字(原 `text-[9px]` |
| `text-2xs`(新增 token | 10px | 小标签 / 数字 / 计数(原 `text-[10px]`,历史最高频) |
| `text-xs` | 12px | 表头 / 小按钮 / 行内说明(统一原 `text-[11px]`/`text-[12px]`/`text-[13px]` |
| `text-sm` | 14px | 默认正文 / UI 组件默认字号 |
| `text-base` | 16px | 强调正文 |
| `text-xl` | 20px | 小标题 |
| `text-2xl`+ | 24px+ | 大标题 / 特殊展示字号,按需在设计评审中确定 |
> 需要在 `app.css` 的 `@theme` 中补充定义:
> ```css
> --text-2xs: 0.625rem; /* 10px */
> --text-3xs: 0.5625rem; /* 9px */
> --text-4xs: 0.5rem; /* 8px */
> ```
> 在此之前,可临时使用 `text-[10px]` 等写法,但一旦 token 落地必须替换。
### 3.3 字重
| Token | 值 | 用途 |
|---|---|---|
| `font-medium` | 500 | UI 组件默认(按钮/CardTitle/DialogTitle |
| **`font-medium-plus`(新增 token** | 650 | Topbar / 主按钮等需要"比 medium 重、比 semibold 轻"的强调场景(原任意值 `font-[650]`,现固化为官方 token,禁止再写 `font-[650]` |
| `font-semibold` | 600 | 强调文本 |
| `font-bold` | 700 | 标题 |
| `font-extrabold` | 800 | 大数字 / 品牌展示 |
> 需在 `@theme` 中补充:`--font-weight-medium-plus: 650;`,并在 Tailwind 中暴露为 `font-medium-plus` 工具类。
### 3.4 行高 / 字间距
- 默认行高 1.5;对话框标题用 `leading-none`;长文本正文(如 `.notebook-md p`)用 `line-height: 1.6`
- 字间距仅用于大写字母标签:`tracking-[0.08em]` ~ `tracking-[0.12em]`,**禁止**在正文中使用额外字间距。
---
## 4. 间距、圆角与布局
### 4.1 基础网格
Tailwind 默认 4px 网格(`0.25rem`),不自定义 `--spacing` 基数。
### 4.2 间距 Token(唯一官方取值)
**规则**:以下为新代码必须优先使用的语义间距 token,**禁止**再随手写 `px-[22px]` 这类任意值。
| Token(新增) | 值 | 用途(替代原任意值) |
|---|---|---|
| `--spacing-page-x` | 22px | 页面左右内边距(原 `px-[22px]` / `mx-[22px]` |
| `--spacing-button-x` | 15px | 主按钮水平内边距(原 `px-[15px]` |
| `--spacing-gap-icon` | 7px | 按钮内图标间距(原 `gap-[7px]` |
| `--spacing-gap-sm` | 5px | 小行距(原 `gap-[5px]` |
| `--spacing-gap-md` | 9px | 列表/区块间距(原 `gap-[9px]` / `gap-[10px]` 就近取整为该 token 或 `gap-2.5` |
其余场景优先使用 Tailwind 内建 token`p-2.5` / `p-3` / `p-4` / `p-6` / `gap-1.5` / `gap-2` / `gap-6` / `px-3` / `py-1`
### 4.3 圆角规范(唯一官方阶梯)
**规则**:新代码**必须**使用以下阶梯,**禁止**再引入新的任意像素圆角。历史任意值按下表强制迁移:
| 官方 Token | 像素 | 用途 | 替代的历史任意值 |
|---|---|---|---|
| `rounded`Tailwind 默认) | 4px | 极小元素(icon 容器等) | `rounded-[4px]` |
| `rounded-md` | 6px | 小型控件、legacy 按钮/输入 | `rounded-[5px]` / `rounded-[6px]` / `rounded-[7px]` |
| `rounded-lg` | 8px | 中等卡片/列表项 | `rounded-[9px]` / `rounded-[10px]` |
| `rounded-xl` | 12px | 大号卡片、Tooltip | `rounded-[11px]` / `rounded-[13px]` |
| `rounded-2xl` | 16px | Textarea / Skeleton | — |
| `rounded-3xl` | 24px | Input / Combobox 弹出层 | — |
| `rounded-4xl` | 32px | Button / Card / Dialog / Drawer / Sheet(最高一级视觉容器) | — |
| `rounded-full` | — | 头像 / 徽章 | — |
> **组件分层原则**:交互控件从小到大遵循 `md → lg → xl → 2xl → 3xl → 4xl` 依次递增,容器级别越大(对话框、卡片)圆角越大,行内小控件(checkbox、badge)圆角越小。新组件设计时先确定"层级",再从表中取值,不要自创中间值。
### 4.4 阴影(唯一官方取值)
| Token | 用途 |
|---|---|
| `shadow-md` | Card 默认阴影 |
| `shadow-lg` | Combobox / 下拉菜单 |
| `shadow-xl` | Dialog / Sheet / Drawer / Sidebar 菜单 |
**规则**:**禁止**新代码使用自定义阴影任意值(如 `shadow-[0_6px_15px_rgb(...)]`)。如确需强调阴影(例如渐变主按钮的"投色阴影"),必须先在本文档中补充为具名 token 后才能使用。
### 4.5 边框
- 默认宽度 `1px`Tailwind `border`)。
- 输入类组件:默认 `border-transparent`focus 时 `focus-visible:border-ring` + `focus-visible:ring-3 focus-visible:ring-ring/30`
- 危险态:`aria-invalid:border-destructive` + `aria-invalid:ring-3 aria-invalid:ring-destructive/20`
- 边框颜色统一使用 `border-border``--color-line`),**禁止**再写 `border-[#dfe6ee]` 等任意 hex(见 §2.7)。
### 4.6 布局
- 页面容器:`min-width: 1120px`,桌面优先布局(当前产品定位不适配移动端,如需移动端适配需局部覆盖,不改全局)。
- 侧栏宽度:展开 `16rem` / 收起 `3rem` / 移动端 `18rem`
- 两栏布局参考:`grid-cols-[310px_minmax(0,1fr)] max-xl:grid-cols-[285px_minmax(0,1fr)]`
- Dashboard 网格参考:`min-[1201px]:grid-cols-[minmax(0,1.6fr)_minmax(310px,0.9fr)] max-[1200px]:grid-cols-1`
### 4.7 断点
使用 Tailwind v4 默认断点:`sm 40rem` / `md 48rem` / `lg 64rem` / `xl 80rem` / `2xl 96rem`,不自定义。响应式优先用 `max-xl:` / `max-[1200px]:` / `sm:` 等既有写法保持一致性。
---
## 5. 组件视觉规范(强制规格,可用任意技术栈实现同等效果)
> 以下为各组件的**强制视觉规格**。使用 React + shadcn 的团队直接复用 `app/components/ui/*`;使用其他框架(Vue/Ant Design/Element Plus 等)的团队,必须在自己的组件层重新实现,但颜色、圆角、尺寸、状态视觉必须与下表完全一致。
### 5.1 Button
- 圆角 `rounded-4xl`;字号默认 `text-sm`;字重 `font-medium`;过渡 `transition-all`
- 尺寸:默认 `h-9 px-3` / `xs h-6 px-2.5` / `sm h-8 px-3` / `lg h-10 px-4` / `icon 36×36` / `icon-xs 24×24` / `icon-sm 32×32` / `icon-lg 40×40`
- 状态:default `bg-primary` + 白字;hover `bg-primary/80`active `translate-y-px`focus `border-ring` + `ring-3 ring-ring/30`disabled `pointer-events-none opacity-50`invalid `border-destructive ring-3 ring-destructive/20`
- 内部 svg 未指定尺寸时自动 `size-4`16px)。
### 5.2 Input
- 圆角 `rounded-3xl`;高度 `h-9`36px);字号 `text-sm`;背景 `bg-input/50`
- 状态:placeholder `text-muted-foreground`focus `border-ring`(无 ring 阴影);disabled `cursor-not-allowed opacity-50`invalid `border-destructive`
### 5.3 Textarea
- 圆角 `rounded-2xl`;最小高 `min-h-16`64px);不可手动 resize;内边距 `px-3 py-3`focus 额外带 `ring-3 ring-ring/30`
### 5.4 Checkbox
- 尺寸 `size-4`16px);圆角 `rounded-md`6px,统一自原 `rounded-[5px]`);背景 `bg-input/90`
- 状态:checked `border-primary bg-primary` + 白色 `CheckIcon`focus `border-ring ring-3 ring-ring/30`disabled `cursor-not-allowed opacity-50`invalid `border-destructive ring-3 ring-destructive/20`
### 5.5 Combobox / Select
- Trigger 与 Input 一致,右侧 `ChevronDownIcon size-4 text-muted-foreground`
- Content:圆角 `rounded-3xl`,背景 `bg-popover`,阴影 `shadow-lg``ring-1 ring-foreground/5`
- Item 右侧固定 `size-4``CheckIcon` 占位槽;Separator `-mx-1.5 my-1.5 h-px bg-border`
- 入场:按 side 滑入 + `fade-in-0 zoom-in-95`,约 100ms。
### 5.6 Card
- 圆角 `rounded-4xl`;背景 `bg-card`;阴影 `shadow-md``ring-1 ring-foreground/5`;内边距通过 `--card-spacing`default 24px / sm 16px)。
- 子区块:`CardTitle``text-base font-medium`/ `CardDescription``text-sm text-muted-foreground`/ `CardContent` / `CardFooter`
### 5.7 Table
- 外包 `div.overflow-x-auto`,表格本体 `w-full text-sm`
- `TableRow``border-b``hover:bg-muted/50`,选中 `data-[state=selected]:bg-muted`
- `TableHead`:高 `h-12`48px),内边距 `px-3``font-medium``whitespace-nowrap`
- `TableCell`:内边距 `p-3`,不换行;`TableCaption``mt-4 text-sm text-muted-foreground`
### 5.8 Dialog / Sheet / Drawer
| 类型 | 圆角 | 阴影 | 背景 | 进场动画 |
|---|---|---|---|---|
| Dialog | `rounded-4xl` | `shadow-xl` + `ring-1 ring-foreground/5` | `bg-popover` | `fade-in-0 zoom-in-95` ~100ms |
| Sheet | `rounded-4xl` | `shadow-xl` | `bg-popover` | 按 side 平移 `2.5rem` ~200ms |
| Drawer | `rounded-4xl border border-popover` | `shadow-xl` | `bg-popover` | `duration-450` + `cubic-bezier(0.22,1,0.36,1)`,支持 swipe |
- 遮罩层统一 `bg-black/30` + `supports-backdrop-filter:backdrop-blur-sm`
- 关闭按钮:右上 `top-4 right-4`icon-sm ghost32×32,浅灰背景),内含 `XIcon`
- **三种动画时长差异属官方设计,禁止拉平统一**:对话框类交互 ≤ 200ms,抽屉/底栏类模拟物理滑动 ≤ 450ms。
### 5.9 AlertDialog
与 Dialog 同布局,唯一差异:标题字号 `text-lg`Dialog 为 `text-base`),用于强调警示内容。
### 5.10 Tooltip
- 圆角 `rounded-xl`;背景 `bg-foreground`(反色);文字 `text-background`;字号 `text-xs`;内边距 `px-3 py-1.5`
- 箭头:`size-2.5 rotate-45 rounded-[2px]`,与背景同色。
- 进场:`fade-in-0 zoom-in-95`,约 100ms,按 side 滑入。
### 5.11 ToastSonner
通过 CSS 变量桥接品牌色,**禁止**脱离变量单独定义 toast 颜色:
```css
--normal-bg: var(--popover);
--normal-text: var(--popover-foreground);
--normal-border: var(--border);
--border-radius: var(--radius);
```
图标统一 `size-4``CircleCheckIcon` / `InfoIcon` / `TriangleAlertIcon` / `OctagonXIcon` / `Loader2Icon animate-spin`
### 5.12 Sidebar
- 宽度:展开 `16rem` / 收起 `3rem` / 移动端 `18rem`
- 深色专属主题:背景 `var(--sidebar-background)`、文字 `var(--sidebar-foreground)`、强调色 `var(--sidebar-primary)`、选中态背景 `var(--sidebar-accent)`(**必须**引用变量,禁止硬编码 hex,见 §2.5)。
- 顶部装饰渐变:`radial-gradient(circle at 10% 1%, rgb(28 105 186 / 25%), transparent 27%), var(--sidebar-background)`
- 收起按钮图标:`PanelLeftIcon`16px)。
### 5.13 Skeleton / Separator
- Skeleton`animate-pulse rounded-2xl bg-muted`
- Separator`shrink-0 bg-border`,水平 `h-px w-full`,垂直 `w-px self-stretch`
### 5.14 InputGroup
`InputGroup` / `InputGroupAddon``inline-start` / `inline-end` / `block-start` / `block-end`/ `InputGroupButton` / `InputGroupText` / `InputGroupInput` / `InputGroupTextarea`addon 点击时焦点转交给兄弟 `<input>`。addon 文本 `text-muted-foreground text-sm font-medium`kbd 元素 `rounded-3xl bg-muted-foreground/10 px-1.5`
### 5.15 Legacy 共享视觉类(仅用于维护存量页面,新代码禁止新增引用)
以下类色值已在本文档中被官方 token 覆盖,**新页面不得再引用这些类**;修改已使用这些类的旧页面时,应顺势替换为 §5.1–§5.14 的现代规格:
| 类 | 视觉规格(供比对迁移) |
|---|---|
| `.icon-button` | 34×34,边框改为 `border-border`,圆角改为 `rounded-md`hover 背景改为 `bg-muted` |
| `.avatar` | 34×34 圆形,渐变改为 `linear-gradient(135deg, var(--color-brand), var(--color-brand-strong))` |
| `.primary-button` | 迁移为 `Button``variant="default"`),渐变改为 `var(--color-brand)``var(--color-brand-deep)` |
| `.secondary-button` | 迁移为 `Button``variant="outline"` |
| `.button-spinner` | 迁移为 `Loader2Icon animate-spin`Toast/Button loading 态统一图标方案,见 §6.1) |
---
## 6. 图标与动效
### 6.1 图标
- 图标库:**`lucide-react`**(线条 1.5px 描边风格),新代码必须使用该图标库;若因技术栈限制需换库,**必须**保证线条粗细、视觉重量与 lucide 一致。
- 尺寸规范:`size-4`16px,按钮/菜单项默认)/ `size-3.5`14pxCheckbox/ `size-2.5`10pxTooltip 箭头)。
- Button 内 svg 未指定尺寸时自动 `size-4`,新实现须遵循此约定。
- 官方高频图标:`XIcon` / `CheckIcon` / `ChevronDownIcon` / `PanelLeftIcon` / `CircleCheckIcon` / `InfoIcon` / `TriangleAlertIcon` / `OctagonXIcon` / `Loader2Icon`。**所有 loading 态一律使用 `Loader2Icon animate-spin`**,禁止再实现新的 spinner 动画(替代原 `.button-spinner` + `spin` keyframes)。
### 6.2 过渡时长(唯一官方取值)
| Token | 用途 |
|---|---|
| `duration-100` | Dialog / AlertDialog / Sonner 进出 |
| `duration-150` | Sheet 遮罩 |
| `duration-200` | Sidebar / Sheet 主体 |
| `duration-450` | Drawer(模拟 iOS 滑动手感,唯一允许超过 200ms 的场景) |
| `duration-0` | 手势拖拽(swipe)中临时禁用过渡 |
**规则**:新增弹层组件必须归类到以上五档之一,不得自创新的时长值。
### 6.3 缓动函数
| Token | 用途 |
|---|---|
| `ease-linear` | 旋转 / 进度条 |
| `ease-in-out` | 通用过渡 |
| `ease-out` | 模态入场 |
| `ease-[cubic-bezier(0.32,0.72,0,1)]` | Drawer 遮罩 |
| `ease-[cubic-bezier(0.22,1,0.36,1)]` | Drawer 主体("expo out"手感) |
### 6.4 自定义关键帧
| keyframes | 时长 / 缓动 | 用途 |
|---|---|---|
| `shimmer` | 1.3s linear infinite | 骨架屏背景流光(`.tree-skeleton-bar` |
| `lock-pulse` | 1.8s ease-in-out infinite | 编辑中状态点呼吸(50% 处 opacity 0.5 + scale 0.78 |
| `modal-in` | 0.18s ease-out | 弹窗从 `translateY(8px) scale(0.985)` 进入 |
| `spin` | 0.7s linear infinite | **已废弃**,新代码统一用 `animate-spin`Tailwind 内建) |
> `toast-in` 已废弃(Sonner 接管),新代码不得再引用。
### 6.5 通用动画工具
- `animate-in` / `animate-out`shadcn tw-animate-css 工具类)。
- `data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95` / `data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95` —— 弹层组件**必须**遵循此统一动效模式。
- `data-[side=...]:slide-in-from-{top|bottom|left|right|inline-start|inline-end}-2` —— 弹出层按侧滑入的唯一官方写法。
- `animate-pulse`Skeleton/ `animate-spin`Loader)。
---
## 7. 命名与代码风格
### 7.1 文件 / 目录
- 路由文件集中在 `app/routes/`,业务页面按域拆到 `app/features/{module}/`
- 共享原语在 `app/components/ui/<name>.tsx`,域内共享在 `app/components/common/<Name>.tsx`
- 工具函数:`app/lib/utils.ts``cn()` = `twMerge(clsx(...))`)。
- 样式入口:**只允许一个 CSS 文件** `app/app.css`,由 `app/root.tsx` 唯一一次 `import "./app.css"` 引入;**禁止**新增分散的样式文件。
### 7.2 组件命名
- UI 组件函数名使用 **PascalCase**`Button` / `CardHeader` / `DialogTitle` / `SidebarMenuButton`
- 每个 UI 元素**必须**带 `data-slot="..."` 属性(如 `button` / `card-header` / `dialog-content`),便于父级选择器和自动化测试定位。
### 7.3 类名拼接
- 统一用 `cn(...)``~/lib/utils`,即 `twMerge(clsx(...))`)拼装类名,**禁止**手写字符串模板拼接类名。
- 组件变体用 `cva(...)` 描述(参考 `Button` / `InputGroupAddon`)。
- 非 Tailwind 技术栈:最终输出的类名/样式必须与 §2–§6 的视觉规格一致。
### 7.4 CSS 类命名约定
- shadcn 风格类**无 BEM**,纯功能化(`card-title` / `dialog-content`),依赖 `data-slot` + Tailwind 工具类定位。
- `@layer utilities` 中的历史类沿用 kebab-case(如 `scrollbar-thin` / `dashboard-hero-shell` 等),**新增**该层的类也必须用 kebab-case。
- **颜色 / 间距 token 优先**:新组件必须优先使用 §2/§4 的语义 token(`bg-primary` / `text-foreground` / `border-border`),**禁止**写裸 hex 或任意值;确无对应 token 时,先补充 token 定义,再使用。
### 7.5 路径别名
```ts
"~": "frontend/app"
"components": "~/components"
"utils": "~/lib/utils"
"ui": "~/components/ui"
"lib": "~/lib"
"hooks": "~/hooks"
```
### 7.6 项目依赖版本(参考,非强制技术选型)
| 角色 | 库 | 版本 |
|---|---|---|
| 原语 | `@base-ui/react` | ^1.7.0 |
| 变体 | `class-variance-authority` | ^0.7.1 |
| 类合并 | `clsx` + `tailwind-merge` | ^2.1.1 / ^3.6.0 |
| 图标 | `lucide-react` | ^1.33.0 |
| 样式 | `tailwindcss` + `@tailwindcss/vite` | ^4.2.2 |
| 动画 | `tw-animate-css` | ^1.4.0 |
| Toast | `sonner` | ^2.0.8 |
| 路由 | `react-router` / `@react-router/dev` | ^8 |
| 状态 | `zustand` | ^5.0.14 |
| 表格 | `@tanstack/react-table` | ^9.1.2 |
> 新模块/新项目如使用其他技术栈(Vue + Naive UI / Element Plus / Ant Design 等),必须按 §2/§3/§4/§6 的视觉规格在自己的主题系统中重新定义等价 token,保持视觉一致。
---
## 8. app.css 需新增的官方 Token(落地清单)
> 本文档 v2.0 相比 v1.0 新增/固化了以下 token**必须**在 `app.css` 的 `@theme` 中补充定义,作为本规范落地的第一步:
```css
@theme {
/* 字号 */
--text-2xs: 0.625rem; /* 10px */
--text-3xs: 0.5625rem; /* 9px */
--text-4xs: 0.5rem; /* 8px */
/* 字重 */
--font-weight-medium-plus: 650;
/* 品牌色补充 */
--color-brand-deep: #0e5bad;
/* 中性色补充 */
--color-ink-caption: #748598;
/* 间距补充 */
--spacing-page-x: 22px;
--spacing-button-x: 15px;
--spacing-gap-icon: 7px;
--spacing-gap-sm: 5px;
--spacing-gap-md: 9px;
}
```
落地后需同步在 Tailwind 中暴露对应工具类(如 `font-medium-plus`),并对 §2.7、§4.3、§5.15 中列出的旧任意值做替换。
---
## 9. 跨技术栈适配指引
若其他系统/新项目**不使用** React + Tailwind + shadcn 技术栈,请按以下方式对齐:
1. **色彩**:将 §2 全部 CSS 变量原样复制为自己主题系统的变量(如 Ant Design 的 `token`、Element Plus 的 CSS 变量、Vue 的 SCSS 变量),变量名可保留 `--color-*` 前缀便于跨项目检索。
2. **字体**:引入同一份 `Inter-Variable.ttf` 字体文件,字号阶梯必须与 §3.2 表格一一对应(值相同,token 名可按自身框架习惯命名,但需在文档中注明与本表的映射关系)。
3. **圆角/间距/阴影**:直接照抄 §4 的像素值,不需要用相同的 Tailwind 类名,但**数值必须相等**。
4. **组件视觉**:对照 §5 逐项还原状态(default/hover/active/focus/disabled/invalid)与尺寸,不要求相同 DOM 结构。
5. **动效**:过渡时长与缓动函数直接照抄 §6.2/§6.3 数值。
6. 完成对齐后,在自己项目的 README 或设计文档中注明"视觉规范参照 STYLE_GUIDE.md vX.X",便于后续版本追踪。
---
## 附:shadcn 配置(参考)
```json
{
"style": "base-luma",
"tsx": true,
"tailwind": { "config": "", "css": "app/app.css", "baseColor": "neutral", "cssVariables": true },
"iconLibrary": "lucide",
"aliases": {
"components": "~/components",
"utils": "~/lib/utils",
"ui": "~/components/ui",
"lib": "~/lib",
"hooks": "~/hooks"
}
}
```
**版本摘要**React/React DOM 19.2.7 · React Router 8 · Tailwind CSS 4.2.2 · `@tailwindcss/vite` 4.2.2 · `@base-ui/react` 1.7.0 · `class-variance-authority` 0.7.1 · `lucide-react` 1.33.0 · `shadcn` CLI 4.19.0 · `sonner` 2.0.8 · `tw-animate-css` 1.4.0 · `tailwind-merge` 3.6.0 · `zustand` 5.0.14。