# 设计令牌专项重构 · 设计文档 > 创建日期:2026-07-04 > 状态:Draft(待用户审查) > 执行方式:大爆炸式(单 PR) > 关联规则:`docs/architecture/004_architecture_impact_map.md`、`.trae/rules/project_rules.md` --- ## 1. 背景与目标 ### 1.1 现状摘要 项目当前令牌散落在 [`src/app/globals.css`](file:///e:/Desktop/CICD/src/app/globals.css)(477 行)与 [`tailwind.config.ts`](file:///e:/Desktop/CICD/tailwind.config.ts)(极简,无 `theme.extend`)。已有 5 类令牌: 1. **shadcn 标准令牌**(HSL 格式,完整暗色) 2. **chart-1~5 数据可视化令牌**(HSL,完整暗色) 3. **sidebar 令牌**(HSL,完整暗色) 4. **`--lp-*` 备课编辑器令牌**(HEX 格式,**无暗色**,**未在 `@theme inline` 中暴露**,无法 `bg-lp-paper`) 5. **Material Design 3 Surface 映射令牌**(在 `@theme inline` 中,**经扫描零使用**,属死代码) ### 1.2 关键技术债 扫描结果: | 问题类型 | 出现次数 | 文件数 | |---|---|---| | 硬编码颜色 `#xxx` | 91 处 | 15 文件 | | 硬编码字体 `'Inter'/'Fraunces'/'JetBrains Mono'` | 8 处 | 2 文件 | | 硬编码 `font-size: Npx` | 15 处 | 3 文件 | | Tailwind 任意值 `w-[Npx]/h-[Npx]/p-[Npx]/...` | 98 处 | 40 文件 | | 缺失:font/spacing/shadow/text-size 尺度令牌 | — | — | | `--lp-*` 无暗色 / 未暴露为 Tailwind 类 | — | — | | M3 Surface 令牌零使用(死代码) | — | — | 项目规则(`.trae/rules/project_rules.md`)明确禁止"任意值"和"动态拼接类名",但实际任意值已散落到 40 个文件,需要专项治理。 ### 1.3 重构目标 - **核心目标**:全量体系化重建设计令牌,建立完整令牌契约,清理全部硬编码与任意值 - **命名体系**:双层架构(Primitive + Semantic)+ shadcn 语义扩展 - **覆盖范围**:font/size/leading/weight/radius/shadow/spacing/duration/ease/z-index 全阶梯 - **暗色覆盖**:明暗双色全面覆盖(所有令牌都有 `:root` 与 `.dark` 双份) - **强制约束**:ESLint 规则 + 注释豁免,防止技术债复发 --- ## 2. 令牌架构 ### 2.1 双层架构 ``` ┌──────────────────────────────────────────────────────────────────┐ │ Layer 1: Primitive (Raw Tokens) — 原始色板/字号/间距/阴影 │ │ ───────────────────────────────────────────────────────────── │ │ 命名: --color-zinc-50 ~ 950 │ │ --color-stone-50 ~ 950 │ │ --color-indigo-500/600 │ │ --font-size-1 ~ --font-size-9 │ │ --space-0 ~ --space-32 │ │ --shadow-1 ~ --shadow-6 │ │ --font-family-sans/serif/mono │ │ 特征: 不直接使用,只被 Semantic 层引用 │ │ 主题: 无主题差异(色板层不区分明暗) │ └──────────────────────────────────────────────────────────────────┘ ▲ 引用 │ ┌──────────────────────────────────────────────────────────────────┐ │ Layer 2: Semantic (Component/Context Tokens) — 语义层 │ │ ───────────────────────────────────────────────────────────── │ │ shadcn 兼容: --background / --foreground / --primary / ... │ │ 扩展层级: --background-elevated / --text-secondary / ... │ │ 模块命名空间: --lp-* (lesson-preparation) │ │ chart/sidebar: --chart-1~5 / --sidebar-* │ │ 特征: 业务代码唯一引用入口,Tailwind 通过 @theme inline 暴露 │ │ 主题: :root 与 .dark 双份定义,所有令牌都有暗色 │ └──────────────────────────────────────────────────────────────────┘ ▲ 引用 │ ┌──────────────────────────────────────────────────────────────────┐ │ Layer 3: Tailwind 类 (via @theme inline) │ │ ───────────────────────────────────────────────────────────── │ │ bg-background / text-foreground / bg-primary / ... │ │ bg-lp-paper / text-lp-inline-node-meta / ... │ │ font-sans / font-serif / font-mono │ │ text-size-2 / leading-snug / weight-semibold │ │ radius-md / shadow-2 / space-4 / duration-fast / z-modal │ └──────────────────────────────────────────────────────────────────┘ ``` ### 2.2 核心原则 - 业务代码 **只引用 Semantic 层**(直接 `var(--background)` 或 Tailwind 类 `bg-background`),禁止引用 Primitive 层 - Primitive 层只为 Semantic 层提供"原材料",便于未来多主题切换(只改 Semantic 层映射,不动 Primitive) - shadcn 命名严格保留(`--background`/`--foreground`/`--primary` 等),`shared/components/ui/*` 零改动 - `--lp-*` 保留命名空间(已有 9 文件使用,改名风险大),但补全暗色与 Tailwind 暴露 ### 2.3 文件分布 当前 [`globals.css`](file:///e:/Desktop/CICD/src/app/globals.css) 477 行,补全令牌后估算 1200+ 行,超过项目规则硬性上限 1000 行。拆分为令牌专题目录: ``` src/app/ ├─ globals.css # 仅保留 @import、base layer、组件硬编码样式清理后的样式 └─ styles/ └─ tokens/ ├─ primitive.css # Layer 1: 原始色板/字号/间距/阴影阶梯 ├─ semantic-light.css # Layer 2: :root 语义令牌(明色) ├─ semantic-dark.css # Layer 2: .dark 语义令牌(暗色) ├─ lesson-preparation.css # --lp-* 令牌(明暗双份) ├─ tailwind-theme.css # @theme inline 暴露规则 └─ index.css # 汇总 @import 入口 ``` `globals.css` 顶部改为: ```css @import "tailwindcss"; @import "./styles/tokens/index.css"; @plugin "tailwindcss-animate"; @plugin "@tailwindcss/typography"; @custom-variant dark (&:where(.dark, .dark *)); ``` ### 2.4 Tailwind v4 集成 [`tailwind.config.ts`](file:///e:/Desktop/CICD/tailwind.config.ts) 保持极简(只 content + plugins),所有令牌通过 `@theme inline` 在 `tailwind-theme.css` 中暴露。 --- ## 3. Primitive 层(原始阶梯) ### 3.1 色板(以 Zinc 为基准中性色,与现有 shadcn 一致) ```css /* primitive.css */ :root { /* Zinc 中性色板(shadcn 默认) */ --color-zinc-50: 0 0% 99%; --color-zinc-100: 240 4.8% 95.9%; --color-zinc-200: 240 5.9% 90%; --color-zinc-300: 240 4.8% 83.9%; --color-zinc-400: 240 5% 64.9%; --color-zinc-500: 240 3.8% 46.1%; --color-zinc-600: 240 5.2% 33.9%; --color-zinc-700: 240 5.3% 26.1%; --color-zinc-800: 240 5.9% 10%; --color-zinc-900: 240 5.9% 3.9%; --color-zinc-950: 240 10% 3.9%; /* Stone 暖灰(用于 lp-* 纸感,与现有 --lp-paper-edge #f8f8f7 对齐) */ --color-stone-50: 60 4.8% 95.9%; --color-stone-100: 60 5.1% 90%; --color-stone-200: 20 5.9% 90%; --color-stone-300: 24 5.7% 82.9%; --color-stone-400: 24 5.4% 63.9%; --color-stone-500: 25 5.1% 44.7%; --color-stone-600: 33 5% 39.8%; --color-stone-700: 30 5.2% 32.7%; --color-stone-800: 12 6.5% 31.4%; --color-stone-900: 24 10% 10%; --color-stone-950: 20 14.3% 4.1%; /* Indigo 强调色(lp-interaction 用) */ --color-indigo-500: 238.7 83.5% 66.7%; --color-indigo-600: 238.6 84.5% 59.8%; } ``` **说明**: 仅记录 Primitive 原料,业务代码不直接引用。 ### 3.2 字号阶梯(替代硬编码 13.5px/14px/12.5px 等) | Token | 值 | 用途 | |---|---|---| | `--font-size-1` | 12px | 元信息/角色标签 | | `--font-size-2` | 13px | inline-node body | | `--font-size-3` | 13.5px | inline-node 主文(备课纸感) | | `--font-size-4` | 14px | 标题/按钮 | | `--font-size-5` | 16px | 正文(Fraunces 16px) | | `--font-size-6` | 18px | H2 | | `--font-size-7` | 20px | H1 | | `--font-size-8` | 24px | 区块标题 | | `--font-size-9` | 32px | 页面标题 | ### 3.3 间距阶梯(覆盖 98 处任意值) | Token | 值 | 替代示例 | |---|---|---| | `--space-0` | 0 | | | `--space-0_5` | 0.125rem (2px) | `p-[2px]` | | `--space-1` | 0.25rem (4px) | `p-[4px]` | | `--space-1_5` | 0.375rem (6px) | `gap-[6px]` | | `--space-2` | 0.5rem (8px) | | | `--space-2_5` | 0.625rem (10px) | `p-[10px]` | | `--space-3` | 0.75rem (12px) | | | `--space-3_5` | 0.875rem (14px) | `p-[14px]` | | `--space-4` | 1rem (16px) | | | `--space-5` | 1.25rem (20px) | `m-[20px]` | | `--space-6` | 1.5rem (24px) | | | `--space-7` | 1.75rem (28px) | `w-[28px]` (lp-tb-btn) | | `--space-8` | 2rem (32px) | | | `--space-10` | 2.5rem (40px) | | | `--space-12` | 3rem (48px) | | | `--space-16` | 4rem (64px) | | | `--space-18` | 4.5rem (72px) | `px-[72px]` (lp-paper-toolbar) | ### 3.4 阴影阶梯(替代硬编码 box-shadow) | Token | 值 | 替代 | |---|---|---| | `--shadow-1` | `0 1px 2px rgba(15,15,15,0.04)` | shadow-xs | | `--shadow-2` | `0 1px 3px rgba(15,15,15,0.06), 0 1px 2px rgba(15,15,15,0.04)` | shadow-sm | | `--shadow-3` | `0 4px 6px rgba(15,15,15,0.05), 0 2px 4px rgba(15,15,15,0.04)` | shadow-md | | `--shadow-4` | `0 1px 2px rgba(15,15,15,0.04), 0 8px 24px rgba(15,15,15,0.04)` | lp-paper-shadow | | `--shadow-5` | `0 1px 2px rgba(15,15,15,0.06), 0 12px 36px rgba(15,15,15,0.08)` | lp-paper-shadow-active | | `--shadow-6` | `0 10px 15px rgba(15,15,15,0.1), 0 4px 6px rgba(15,15,15,0.05)` | shadow-xl | ### 3.5 字体家族(替代 8 处硬编码) | Token | 值 | 用途 | |---|---|---| | `--font-family-sans` | `'Inter', system-ui, sans-serif` | UI 元素 | | `--font-family-serif` | `'Fraunces', Georgia, serif` | 主文/备课纸感 | | `--font-family-mono` | `'JetBrains Mono', monospace` | 角色标签 | --- ## 4. Semantic 层(shadcn 扩展 + lp-* 升级 + M3 清理) ### 4.1 shadcn 标准令牌(零改动,确保 ui/ 兼容) `--background`/`--foreground`/`--card`/`--popover`/`--primary`/`--secondary`/`--muted`/`--accent`/`--destructive`/`--border`/`--input`/`--ring`/`--radius` 全部保留现有 HSL 值,`:root` 与 `.dark` 双份定义不变。 ### 4.2 语义层扩展(新增层级) | Token | 明色 HSL | 暗色 HSL | 用途 | |---|---|---|---| | `--background-elevated` | `0 0% 100%` | `240 6% 10%` | 浮层/card-on-card | | `--background-sunken` | `240 4.8% 95.9%` | `240 6% 8%` | 凹陷区/inset | | `--text-primary` | `= --foreground` | `= --foreground` | 主文 | | `--text-secondary` | `240 3.8% 46.1%` | `240 5% 64.9%` | 副文(= muted-foreground) | | `--text-tertiary` | `240 4% 65%` | `240 5% 50%` | 占位/元信息 | | `--border-strong` | `240 5.9% 70%` | `240 5% 40%` | 强调边框 | | `--border-subtle` | `240 5.9% 95%` | `240 5% 18%` | 弱边框 | **说明**: `--text-secondary` 等是 `--muted-foreground` 的语义别名,业务代码优先用语义名。 ### 4.3 chart-1~5 / sidebar 令牌(零改动) 保留现有 HSL 值,`:root` 与 `.dark` 双份定义不变。 ### 4.4 --lp-* 令牌升级(HEX→HSL + 暗色 + Tailwind 暴露) **明色(`:root`)**: | Token | 旧 HEX | 新 HSL | 说明 | |---|---|---|---| | `--lp-paper` | `#fefefe` | `0 0% 99.6%` | 纸面 | | `--lp-paper-edge` | `#f8f8f7` | `60 9% 97%` | 纸边(对齐 stone-100) | | `--lp-paper-shadow` | (box-shadow) | `= --shadow-4` 引用 | 阴影令牌化 | | `--lp-paper-shadow-active` | (box-shadow) | `= --shadow-5` 引用 | 阴影令牌化 | | `--lp-anchor-range` | `rgba(28,25,23,0.08)` | `20 14.3% 4.1% / 0.08` | 引用 stone-950 | | `--lp-anchor-range-active` | `rgba(28,25,23,0.16)` | `20 14.3% 4.1% / 0.16` | | | `--lp-anchor-point` | `#1c1917` | `20 14.3% 4.1%` | = stone-950 | | `--lp-inline-node-border` | `#d6d3d1` | `30 5.7% 82.9%` | = stone-300 | | `--lp-inline-node-text` | `#44403c` | `24 5.4% 26.9%` | ≈ stone-700 微调 | | `--lp-inline-node-meta` | `#a8a29e` | `30 5% 64.9%` | = stone-400 | | `--lp-interaction` | `#6366f1` | `238.6 84.5% 59.8%` | = indigo-600 | **12 个 --lp-dot-* 节点色点**: 全部映射到 zinc/stone 色板对应值,统一为 HSL。 **暗色(`.dark`)**: 全部补齐。例如: - `--lp-paper: 240 6% 10%`(暗背景纸面) - `--lp-paper-edge: 240 6% 8%` - `--lp-anchor-point: 0 0% 98%`(反相) - `--lp-interaction: 238.7 83.5% 66.7%`(= indigo-500,暗色微亮) ### 4.5 M3 Surface 映射令牌清理 [`globals.css:185-198`](file:///e:/Desktop/CICD/src/app/globals.css#L185-L198) 中的 M3 令牌零使用(已扫描确认),全部删除: - `--color-surface` / `--color-on-surface` / `--color-on-surface-variant` - `--color-surface-container-lowest/low/high/highest` - `--color-outline-variant` / `--color-outline` - `--color-error` / `--color-tertiary` / `--color-tertiary-container` ### 4.6 globals.css 硬编码样式令牌化 | 硬编码 | 令牌化后 | |---|---| | `.lp-tb-btn { width: 28px; ... font-family: 'Inter'; font-size: 13px; color: var(--foreground) }` | `width: var(--space-7); font-family: var(--font-family-sans); font-size: var(--font-size-2); color: hsl(var(--foreground))` | | `.lp-inline-node { font-family: 'Inter'; font-size: 13.5px; ... border-left: 2px solid var(--lp-inline-node-border); color: var(--lp-inline-node-text) }` | 全部用令牌引用 | | `.lp-qa-role.teacher { color: #1c1917 }` | `color: hsl(var(--lp-anchor-point))` | | `.lp-qa-role.student { color: #6b7280 }` | `color: hsl(var(--color-zinc-500))` | | `.lp-inline-node-title { color: #1a1a1a }` | `color: hsl(var(--lp-anchor-point))` | | `.lp-inline-node-body { color: #404040 }` | `color: hsl(var(--lp-inline-node-text))` | | `.lp-qa-content { color: #404040 }` | 同上 | | `.lp-qa-prompt { color: #525252 }` | `color: hsl(var(--color-zinc-600))` | | `.range-anchor { background-color: var(--node-color, #1976d2) }` | fallback 改为 `hsl(var(--lp-interaction))` | --- ## 5. 尺度令牌补齐清单 ### 5.1 圆角阶梯 | Token | 值 | Tailwind 类 | |---|---|---| | `--radius-sm` | `calc(var(--radius) - 4px)` | `rounded-sm` | | `--radius-md` | `calc(var(--radius) - 2px)` | `rounded-md` | | `--radius-lg` | `var(--radius)` = `0.5rem` | `rounded-lg` | | `--radius-xl` | `calc(var(--radius) + 4px)` | `rounded-xl` | | `--radius-2xl` | `calc(var(--radius) + 8px)` | `rounded-2xl` | | `--radius-full` | `9999px` | `rounded-full` | ### 5.2 行高与字重 | Token | 值 | |---|---| | `--leading-tight` | 1.2 | | `--leading-snug` | 1.35 | | `--leading-normal` | 1.5 | | `--leading-relaxed` | 1.65 | | `--leading-loose` | 1.8 | | `--weight-regular` | 400 | | `--weight-medium` | 500 | | `--weight-semibold` | 600 | | `--weight-bold` | 700 | ### 5.3 动效与 z-index | Token | 值 | Tailwind 类 | |---|---|---| | `--duration-fast` | 150ms | `duration-fast` | | `--duration-normal` | 200ms | `duration-normal` | | `--duration-slow` | 300ms | `duration-slow` | | `--ease-in` | `cubic-bezier(0.4, 0, 1, 1)` | `ease-in` | | `--ease-out` | `cubic-bezier(0, 0, 0.2, 1)` | `ease-out` | | `--ease-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | `ease-in-out` | | `--z-dropdown` | 1000 | `z-dropdown` | | `--z-sticky` | 1100 | `z-sticky` | | `--z-modal` | 1300 | `z-modal` | | `--z-popover` | 1400 | `z-popover` | | `--z-toast` | 1500 | `z-toast` | --- ## 6. 硬编码清理映射表(分级) ### 6.1 91 处 #hex 颜色 — 按文件分类 | 文件 | 处数 | 清理策略 | |---|---|---| | `app/globals.css` | 32 | 全部令牌化(见 4.6 节) | | `app/manifest.ts` | 2 | 保留(theme_color 元数据,需字面量),加注释 `// arbitrary-value: PWA manifest requires literal hex` | | `app/layout.tsx` | 2 | 检查后令牌化或注释 | | `shared/components/ui/chart.tsx` | 5 | 令牌化(用 `--chart-1~5`) | | `modules/notifications/channels/email-channel.ts` | 7 | 保留(邮件 HTML 内联样式需字面量),加注释 | | `modules/textbooks/components/knowledge-graph.tsx` | 10 | 令牌化(用 `--chart-*` 或新增 `--graph-node-*`) | | `modules/textbooks/components/force-graph.tsx` | 17 | 同上 | | `modules/lesson-preparation/components/paper-editor/*` | ~6 | 令牌化(用 `--lp-*`) | | `modules/attendance/components/attendance-grade-correlation-card.tsx` | 3 | 令牌化(用 `--chart-*`) | | `modules/classes/components/my-classes-grid.tsx` | 2 | 令牌化 | | `modules/lesson-preparation/components/version-diff-view.tsx` | 1 | 令牌化(用 `--lp-*` 或新增 `--diff-add`/`--diff-remove`) | **新增语义令牌**: - `--diff-add` / `--diff-add-bg`(版本对比新增) - `--diff-remove` / `--diff-remove-bg`(版本对比删除) - `--graph-node-1` ~ `--graph-node-6`(知识图谱节点色) ### 6.2 8 处硬编码 font-family — 全部令牌化 | 文件 | 处数 | 令牌化 | |---|---|---| | `app/globals.css` | 7 | `var(--font-family-sans)` / `var(--font-family-serif)` / `var(--font-family-mono)` | | `modules/lesson-preparation/components/paper-editor/textbook-tiptap-editor.tsx` | 1 | 同上 | ### 6.3 15 处硬编码 font-size — 全部令牌化 | 文件 | 处数 | 令牌化 | |---|---|---| | `app/globals.css` | 13 | `var(--font-size-N)` | | `modules/notifications/channels/email-channel.ts` | 1 | 保留(邮件 HTML),加注释 | | `modules/lesson-preparation/components/paper-editor/textbook-tiptap-editor.tsx` | 1 | `var(--font-size-N)` | ### 6.4 98 处 Tailwind 任意值 — 分级清理 **Tier 1 — 直接映射 Tailwind 默认阶梯**(约 40 处): - `w-[28px]` → `w-7`(28px ≈ 1.75rem,但 w-7 = 1.75rem ✓) - `gap-[6px]` → `gap-1.5`(6px = 0.375rem,gap-1.5 ✓) - `p-[10px]` → 需新增 `--space-2_5`(10px = 0.625rem) - `text-[13px]` → `text-size-2`(13px = `--font-size-2`) **Tier 2 — 新增间距令牌后引用**(约 30 处): - `w-[72px]` → `w-space-18`(`--space-18` = 4.5rem) - `h-[16px]` → `h-space-4`(`--space-4` = 1rem) - `p-[20px]` → `p-space-5`(`--space-5` = 1.25rem) **Tier 3 — 注释豁免**(约 28 处): - 图表 SVG 尺寸(如 `w-[400px]` 在 chart 容器)→ 加注释 `// arbitrary-value: chart canvas fixed size` - 邮件 HTML 内联样式 - loading.tsx 占位骨架特殊尺寸 **ESLint 规则**: `tailwindcss/no-arbitrary-value: error`,允许通过 `// eslint-disable-next-line` + 注释理由豁免。 --- ## 7. 强制约束机制 ### 7.1 ESLint 规则新增 **新增依赖**: `eslint-plugin-tailwindcss`(需验证与 Tailwind v4 兼容性,若不兼容则自定义规则) **`eslint.config.mjs` 新增规则**: ```javascript { // 禁止 Tailwind 任意值(允许 eslint-disable + 注释豁免) 'tailwindcss/no-arbitrary-value': 'error', // 禁止 TSX/CSS 中硬编码颜色字面量(白名单:globals.css primitive 定义、email-channel.ts、manifest.ts) 'no-restricted-syntax': [ 'error', { selector: "Literal[value=/#[0-9a-fA-F]{3,8}/]", message: '禁止硬编码 hex 颜色,使用设计令牌 var(--*) 或 Tailwind 类 bg-*', }, ], } ``` **自定义规则文件**: `eslint-rules/no-hardcoded-design-tokens.js` - 检测 TSX/TS 中 `'Inter'`/`'Fraunces'`/`'JetBrains Mono'` 字面量 - 检测 `font-family:` CSS 属性中的字面量字体名 - 检测 `font-size: Npx` 硬编码 - 白名单:`globals.css`、`styles/tokens/*.css`、`email-channel.ts`、`manifest.ts` ### 7.2 项目规则更新 [`.trae/rules/project_rules.md`](file:///e:/Desktop/CICD/.trae/rules/project_rules.md) "Tailwind 规范"章节新增: ```markdown ### 设计令牌规范(强制) - **禁止硬编码颜色**: TSX/TS/CSS 中不得出现 `#hex` 颜色字面量,统一使用 `var(--*)` 或 Tailwind 类 - **禁止硬编码字体**: 不得出现 `'Inter'`/`'Fraunces'`/`'JetBrains Mono'` 字面量,使用 `var(--font-family-sans/serif/mono)` - **禁止硬编码字号**: 不得出现 `font-size: Npx`,使用 `var(--font-size-1~9)` - **禁止 Tailwind 任意值**: 不得使用 `w-[Npx]`/`h-[Npx]`/`p-[Npx]` 等,映射到 `--space-*` 或 Tailwind 默认阶梯 - **豁免场景**(需 `// arbitrary-value: ` 注释): - PWA manifest(`app/manifest.ts`) - 邮件 HTML 内联样式(`email-channel.ts`) - 图表 SVG 固定画布尺寸 - loading.tsx 占位骨架 - **令牌文件分布**: `src/app/styles/tokens/`(primitive/semantic-light/semantic-dark/lesson-preparation/tailwind-theme) - **改令牌必同步图**: 修改令牌定义后,同步更新 `docs/architecture/004` 与 `005` ``` ### 7.3 Lint 命令不变 `npm run lint` 与 `npx tsc --noEmit` 保持零错误零警告。 --- ## 8. 验证策略与架构图同步 ### 8.1 验证清单 | 验证项 | 命令/方法 | 通过标准 | |---|---|---| | TypeScript | `npx tsc --noEmit` | 零错误 | | ESLint | `npm run lint` | 零错误零警告 | | 视觉回归 | `npm run test:visual` | 快照无差异(或更新后人工确认) | | 备课编辑器视觉 | 手动检查 `teacher/lesson-plans/[id]/edit` | 纸感、inline-node、师生对话样式无变化 | | 暗色模式 | 切换暗色主题 | 所有页面可读,无对比度问题 | | shadcn ui 组件 | 抽查 button/card/dialog/table | 零回归 | ### 8.2 架构图同步(项目规则强制) 修改完成后,同步更新: | 文档 | 同步内容 | |---|---| | [`docs/architecture/004_architecture_impact_map.md`](file:///e:/Desktop/CICD/docs/architecture/004_architecture_impact_map.md) | 新增"设计令牌体系"章节(双层架构图、文件分布、令牌分类) | | [`docs/architecture/005_architecture_data.json`](file:///e:/Desktop/CICD/docs/architecture/005_architecture_data.json) | `modules.shared.exports` 新增 `design-tokens` 节点;`dependencyMatrix` 新增令牌依赖关系 | | [`docs/troubleshooting/known-issues.md`](file:///e:/Desktop/CICD/docs/troubleshooting/known-issues.md) | 新增"设计令牌"问题分类,记录 Tailwind v4 + ESLint 兼容性、HEX→HSL 转换、任意值豁免规则 | ### 8.3 大爆炸式执行顺序(单 PR 内) 1. 创建 `src/app/styles/tokens/` 6 个文件(primitive/semantic-light/semantic-dark/lesson-preparation/tailwind-theme/index) 2. `globals.css` 改为 `@import` 引入,删除已迁移内容 3. `globals.css` 中剩余硬编码样式令牌化(lp-tb-btn/lp-inline-node/lp-qa-*) 4. 全局扫描替换:91 处 `#hex` → 令牌引用(按 6.1 表) 5. 全局扫描替换:8 处 font-family + 15 处 font-size → 令牌引用(按 6.2/6.3 表) 6. 全局扫描替换:98 处任意值 → Tier 1/2/3 分级处理(按 6.4 表) 7. ESLint 规则新增 + 自定义规则文件 8. 项目规则文档更新 9. 架构图同步(004/005) 10. `docs/troubleshooting/known-issues.md` 更新 11. `npm run lint` + `npx tsc --noEmit` + `npm run test:visual` 验证 12. 单次 commit:`refactor(design-tokens): 全量体系化重建设计令牌` --- ## 9. 风险与缓解 | 风险 | 缓解 | |---|---| | Tailwind v4 + `eslint-plugin-tailwindcss` 兼容性未知 | 实施时先验证,不兼容则自定义 AST 规则 | | 大爆炸式改动 50+ 文件,review 困难 | 严格按 8.3 执行顺序,每步独立验证;视觉回归测试保障 | | `--lp-*` HEX→HSL 转换可能有色差 | 视觉回归测试 + 手动备课编辑器检查 | | 98 处任意值分级清理可能遗漏 | Tier 3 注释豁免需明确理由,ESLint 规则锁定 | | 暗色补全可能对比度不足 | 暗色模式手动抽查所有页面 | | shadcn ui 组件理论零改动但需验证 | 抽查 button/card/dialog/table 4 个核心组件 | --- ## 10. 范围与非目标 ### 10.1 范围内 - 令牌底座重建(Primitive + Semantic 双层) - 尺度令牌补齐(font/size/leading/weight/radius/shadow/spacing/duration/ease/z-index) - `--lp-*` 升级(HEX→HSL + 暗色 + Tailwind 暴露) - M3 Surface 死代码清理 - 91 处硬编码颜色清理 - 8 处硬编码 font-family 清理 - 15 处硬编码 font-size 清理 - 98 处 Tailwind 任意值清理 - ESLint 强制约束规则 - 项目规则文档更新 - 架构图同步(004/005) - known-issues.md 更新 ### 10.2 范围外(非目标) - 第三主题(如 high-contrast、paper 纸感)预留 — YAGNI - `shared/components/ui/*` shadcn 组件内部样式改动 — 理论零改动,仅验证 - `cn()` 工具函数修改 — 不变 - `tailwind.config.ts` 内容扩展 — 保持极简 - 设计令牌文档站(Storybook 等)— 不在本期范围 - 多主题切换架构 — 仅明暗双色