Primitive + Semantic 双层令牌架构,HEX->HSL,明暗双份,@theme inline 暴露为 Tailwind 类。 - 新建 src/app/styles/tokens/ 6 个令牌文件(primitive/semantic-light/semantic-dark/lesson-preparation/tailwind-theme/index) - globals.css 改为 @import 引入,477->258 行 - 清理 91 处 #hex 硬编码颜色 -> hsl(var(--*)) - 清理 10 处硬编码字体 -> var(--font-family-*) - 清理 100 文件 Tailwind 任意值(Tier 1 映射/Tier 3 注释豁免) - 清理 M3 Surface 死代码,升级 --lp-* 令牌(HEX->HSL + 暗色补全) - 新建 ESLint 自定义规则 no-hardcoded-design-tokens(单词边界正则) - eslint.config.mjs 新增 no-restricted-syntax 禁止 #hex + 自定义规则加载(pathToFileURL) - 项目规则新增设计令牌规范强制章节 - 架构图 004/005 同步设计令牌体系节点 - known-issues.md 追加设计令牌问题分类(7 个规则表) 验证: tsc --noEmit 0 errors, npm run lint 0 errors/12 warnings(均为既有问题)
25 KiB
设计令牌专项重构 · 设计文档
创建日期:2026-07-04 状态:Draft(待用户审查) 执行方式:大爆炸式(单 PR) 关联规则:
docs/architecture/004_architecture_impact_map.md、.trae/rules/project_rules.md
1. 背景与目标
1.1 现状摘要
项目当前令牌散落在 src/app/globals.css(477 行)与 tailwind.config.ts(极简,无 theme.extend)。已有 5 类令牌:
- shadcn 标准令牌(HSL 格式,完整暗色)
- chart-1~5 数据可视化令牌(HSL,完整暗色)
- sidebar 令牌(HSL,完整暗色)
--lp-*备课编辑器令牌(HEX 格式,无暗色,未在@theme inline中暴露,无法bg-lp-paper)- 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 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 顶部改为:
@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 保持极简(只 content + plugins),所有令牌通过 @theme inline 在 tailwind-theme.css 中暴露。
3. Primitive 层(原始阶梯)
3.1 色板(以 Zinc 为基准中性色,与现有 shadcn 一致)
/* 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 中的 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 新增规则:
{
// 禁止 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 "Tailwind 规范"章节新增:
### 设计令牌规范(强制)
- **禁止硬编码颜色**: 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: <reason>` 注释):
- 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 |
新增"设计令牌体系"章节(双层架构图、文件分布、令牌分类) |
docs/architecture/005_architecture_data.json |
modules.shared.exports 新增 design-tokens 节点;dependencyMatrix 新增令牌依赖关系 |
docs/troubleshooting/known-issues.md |
新增"设计令牌"问题分类,记录 Tailwind v4 + ESLint 兼容性、HEX→HSL 转换、任意值豁免规则 |
8.3 大爆炸式执行顺序(单 PR 内)
- 创建
src/app/styles/tokens/6 个文件(primitive/semantic-light/semantic-dark/lesson-preparation/tailwind-theme/index) globals.css改为@import引入,删除已迁移内容globals.css中剩余硬编码样式令牌化(lp-tb-btn/lp-inline-node/lp-qa-*)- 全局扫描替换:91 处
#hex→ 令牌引用(按 6.1 表) - 全局扫描替换:8 处 font-family + 15 处 font-size → 令牌引用(按 6.2/6.3 表)
- 全局扫描替换:98 处任意值 → Tier 1/2/3 分级处理(按 6.4 表)
- ESLint 规则新增 + 自定义规则文件
- 项目规则文档更新
- 架构图同步(004/005)
docs/troubleshooting/known-issues.md更新npm run lint+npx tsc --noEmit+npm run test:visual验证- 单次 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 等)— 不在本期范围
- 多主题切换架构 — 仅明暗双色