Files
ycode/docs/设计系统提取与统一方案-2026-10-18.md
xiaohan.yuan e73c59b10e docs: add design system extraction plan
- record current token fragmentation, component inventory, target layering, and migration order
- note the stale verify-ui bridge as a P0 prerequisite
2026-10-18 18:10:23 +08:00

11 KiB
Raw Permalink Blame History

YCode 设计系统提取与组件统一方案(2026-10-18)

结论

可以,而且当前代码已经具备做这件事的基础,但不能直接从现有 CSS 里“提炼一套 token”后全局替换。

原因是 YCode 现在不是一套设计系统,而是四套来源同时生效:

  1. --yc-*:YCode 自有的根级语义 token,主要在 src/renderer/src/styles/tokens.css。
  2. --wb-*:从 WorkBuddy 恢复的 token,但没有全局根定义,而是按组件作用域分别复制。
  3. --cb-* / --cr-* / --dc-* / --ec-* / --wb-kb-*:会话、能力中心、项目页等恢复模块各自的主题闭包。
  4. 少量本地 .btn / .input / .dialog 等旧类名,和恢复 UI 的 wb-button、wb-input、wb-modal 并存。

因此正确的路径不是“抽一套 token 全量替换”,而是:

冻结恢复层,收敛本地层;把 --wb-* 明确降级为“原版兼容层”,把 --yc-* 提升为“YCode 自有界面的唯一语义层”;在语义层之上再抽通用组件。

现状证据

规模

统计范围:src/renderer/src 下的 .css / .tsx / .ts。

指标 数值
CSS 文件 109
TSX 文件 151
CSS 总行数 19,899
CSS 声明 63,042
裸 px 16,103
十六进制颜色 13,008
!important 925
--yc-* 定义 / 使用 246 / 1,372
--wb-* 定义 / 使用 2,129 / 22,756
导出的大写组件名 350(其中大量是图标组件)

构建产物 out/renderer/assets/*.css 中:

  • 规则数 14,082,!important 934。
  • 声明 --wb-* 变量 21,935 次,--yc-* 仅 314 次。
  • 大多数 --wb-* 来自按作用域复制的原版闭包,而不是一个全局主题。

样式分层

层 文件 现状
核心 token src/renderer/src/styles/tokens.css --yc-* 有完整的颜色、间距、字号、圆角、阴影、布局定义;根级生效
排版 token src/renderer/src/workbuddy/typography/tokens.css REA 校验过的 --wb-* 排版 token,但不含颜色/圆角/控件 token
恢复 source src/renderer/src/**/source-*.css、*-source.css 50 个文件、约 2.14 MB,原版规则与局部 token 闭包
恢复 recovered src/renderer/src/**/*-recovered.css 17 个文件、约 0.68 MB,生成的页面级投影
本地适配 **/*-adapter.css、**/*-adapters.css 7 个文件、约 32 KB
YCode 自有页面 styles/app.css、styles/sidebar.css、components/*.css、library/*.css、automations/*.css、projects/*.css 27 个本地文件、约 0.47 MB

同名 token 在不同子树解析出不同来源

--wb-palette-brand-8 至少在这些作用域被分别重新声明:

  • src/renderer/src/capabilities/capability-center-source.css:2(.yc-capability-center)
  • src/renderer/src/components/model-settings.css:146(.models-settings-panel)
  • src/renderer/src/components/source-home-recovered.css:2(.source-home)
  • src/renderer/src/components/source-workspace-picker-recovered.css:2(.source-workspace-picker)
  • src/renderer/src/workbuddy/conversation/source-styles.css(.chat-pane.cr-theme 及其 dark/light 分支)

这意味着:

  • 全局根上并没有 --wb-palette-* / --wb-color-* 的单一权威来源。
  • 页面在自己的 subtree 内看起来是对的,但一旦组件被移出原作用域,就会掉色、掉圆角或掉主题。
  • 新组件如果引用 --wb-*,只能碰运气;引用 --yc-* 才有全局保证。

根级 --yc-* 已在渲染态生效

对当前构建产物 out/renderer/index.html 做了只读渲染抽查:

变量 计算值
--yc-text-primary rgba(0, 0, 0, 0.9)
--yc-bg-primary #ffffff
--yc-radius-lg 8px

当前 scripts/verify-ui.mjs 的假桥已落后于生产渲染树,缺少 window.ycode.library,因此首页/设置只能加载到 token,不能完整挂载页面。这是 P0 需要修的工具问题,不是产品 CSS 问题。

组件重复度

TSX 中的原生控件数量:

元素 数量
<button> 362
<input> 48
<textarea> 8
<select> 2
<svg> 164

类名使用:

模式 出现次数
wb-button 53
wb-button--medium 37
wb-button__content 24
wb-button--secondary 21
wb-icon 17
wb-button--primary 12
本地 .btn 10

本地还存在独立的:

  • .btn / .btn--primary / .btn--ghost / .btn-icon:src/renderer/src/styles/app.css
  • .automation-button / .automation-button--secondary / .automation-input:src/renderer/src/automations/automation-page.css
  • library-*、project-*、memory-modal、help-feedback-panel 等各自的按钮/输入/弹窗样式

也就是说:控件没有被统一;它们只是分别长得像原版。

目标架构

采用三层 token + 一层兼容桥,禁止跨层直连。

L0 基础层

--yc-palette-*、--yc-space-*、--yc-radius-*、--yc-font-size-*、--yc-size-*、--yc-motion-*

  • 只在 styles/tokens.css 声明。
  • 任何页面、组件、adapter 都不得重新声明 L0。
  • 深色主题只允许在 html[data-theme='dark'] 下重映射。

L1 语义层

--yc-text-*、--yc-bg-*、--yc-border-*、--yc-status-*、--yc-elevation-*

  • 组件只消费 L1,不直接消费 L0 的颜色值。
  • 语义层负责浅色/深色切换。

L2 组件层

--yc-button-*、--yc-input-*、--yc-dialog-*、--yc-menu-*、--yc-tabs-*、--yc-card-*

  • 只在 ui/ 组件样式中声明。
  • 页面只能通过组件 props 或变体影响外观,不能重写内部尺寸。

兼容层

--wb-*、--cb-*、--cr-*

  • 只服务 REA 恢复的 source/recovered 模块。
  • 在根或明确的 host scope 上做一次桥接:--wb-bg-primary: var(--yc-bg-primary) 等。
  • 不再允许新页面按块复制整套 --wb-palette-*。
  • 恢复文件本身不改:它们由 scripts/rea/recover-*.mjs 生成,手改会被下一次生成覆盖。

目标组件清单

按优先级抽取。优先复用已有实现,不重写行为。

优先级 组件 复用来源
P1 Button / IconButton wb-button 变体、本地 .btn
P1 Input / Textarea wb-input、automation-input、设置页输入
P1 Select 已有 SourceSelect / RecoveredSelect
P1 Dialog / ConfirmDialog wb-modal、task-confirm-dialog
P1 Popover / Menu wb-popover、wb-dropdown、SourceSelectPopover
P1 Tabs / Segmented wb-segmented、设置导航、能力中心导航
P2 Checkbox / Switch wb-checkbox、wb-switch
P2 Card 专家卡、项目卡、设置分组卡
P2 ListItem / NavItem 侧栏、选择器、列表页
P2 Badge / Tag Beta、状态、文件类型
P2 EmptyState 侧栏空态、资料库空态、项目空态
P3 Avatar / MemberStatus 已有 MemberAvatar / MemberStatus
P3 Spinner / Skeleton 设置加载、资料库骨架、能力加载
P3 Tooltip wb-tooltip、RecoveredSelectTooltip
P3 Toast / Notice 未接入提示、错误提示、更新提示

图标不进入第一轮统一:164 个 <svg> 分散在十多个 *Icons.tsx 和恢复模块中,先把按钮/输入/弹窗收敛,收益更大、风险更低。

迁移顺序

采用绞杀者模式(strangler fig),禁止一次性替换全部 CSS。

P0 冻结与可观测

  • 新增 design-system.md,记录实际生效的 token、组件清单、例外与恢复模块边界。
  • 修正 scripts/verify-ui.mjs 的假桥,使首页、设置、至少一个非聊天页面可以完整挂载。
  • 建立 6 个基准面的浅色/深色截图:首页、会话、设置、能力中心、资料库/自动化、项目。
  • 新增静态检查:新增文件禁止裸 hex、裸 px、裸 !important;存量文件设白名单和递减额度。

验收:能对同一页面在迁移前后做计算样式归档和截图对比。

P1 收敛主题定义

  • 在根上补齐 --wb-* -> --yc-* 桥接,覆盖背景、文字、边框、状态、圆角、控件尺寸。
  • 按页面逐个删除块级 --wb-palette-* / --wb-color-* 复制,只保留原版恢复模块真正依赖的闭包。
  • 固定 CSS 加载顺序:typography -> yc tokens -> bridge -> app -> 组件/页面适配 -> 恢复 source。恢复 source 不再依赖 @import 的隐式顺序。
  • 每个页面迁移后必须验证浅色、深色和窄窗。

验收:根级 --wb-* 与 --yc-* 解析到同一语义值;页面内不再出现第二份调色板。

P2 抽 P1 组件

  • 先做 Button、Input、Select、Dialog、Popover、Tabs。
  • 组件接受 className 并合并到根元素,不把 margin 写进组件。
  • 变体只覆盖已有变体:primary / secondary / grey / ghost / link / danger,尺寸只覆盖 small / medium / large。

验收:同一组件在至少 3 个不同页面替换后,截图零回归或差异有明确解释。

P3 迁移 YCode 自有页面

顺序建议:

  1. 设置页(最多重复的输入/弹窗/分组)
  2. 侧栏与标题栏
  3. 自动化、资料库、项目
  4. 能力中心宿主壳
  5. 首页与输入区外壳

恢复的会话渲染、工具卡、Mermaid、代码块、Diff 暂不迁移,只通过外层适配器接入新 token。

P4 清理

  • 删除本地 .btn / .input / .dialog 等旧类名。
  • 合并重复选择器,清掉无主题差异的 !important。
  • 更新 design-system.md,重新跑提取。

不做什么

  • 不手改 *-recovered.css 和 REA 生成脚本的产物。
  • 不重写会话、工具卡、Diff、Mermaid 等已经过 REA 对齐的恢复模块。
  • 不在这一轮改动 WorkBuddy 原版采集通道。
  • 不为了“统一”把原版恢复层的具体尺寸改成 --yc-*;先桥接语义色和主题,几何差异以 REA 证据为准。

风险

风险 说明 应对
破坏 1:1 恢复模块依赖当前作用域和层叠顺序 先桥接、后替换;恢复文件只读
主题回归 浅色/深色由不同 scope 各写一遍 先做根桥接,再做页面迁移
!important 冲突 925 处,主要集中在恢复层 不全局清理,只清本地层
组件行为回归 Select、弹窗、菜单有交互状态 复用现有实现,只抽外观壳
工具失真 verify-ui.mjs 假桥已落后 P0 先修,否则无法做渲染验收

建议的第一批交付

如果现在开始,建议第一批只做四件事:

  1. design-system.md:现状 + 目标 token 表 + 组件清单 + 例外。
  2. 根级 --wb-* 桥接:先让语义色和主题归一。
  3. Button / Input / IconButton 三个基础组件。
  4. 迁移设置页作为试点,跑浅色/深色/窄窗截图和回归。

这一批完成后,后续页面可以按同一模式复制,不需要再讨论架构。