- record current token fragmentation, component inventory, target layering, and migration order - note the stale verify-ui bridge as a P0 prerequisite
11 KiB
YCode 设计系统提取与组件统一方案(2026-10-18)
结论
可以,而且当前代码已经具备做这件事的基础,但不能直接从现有 CSS 里“提炼一套 token”后全局替换。
原因是 YCode 现在不是一套设计系统,而是四套来源同时生效:
--yc-*:YCode 自有的根级语义 token,主要在src/renderer/src/styles/tokens.css。--wb-*:从 WorkBuddy 恢复的 token,但没有全局根定义,而是按组件作用域分别复制。--cb-*/--cr-*/--dc-*/--ec-*/--wb-kb-*:会话、能力中心、项目页等恢复模块各自的主题闭包。- 少量本地
.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,
!important934。 - 声明
--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.csslibrary-*、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 自有页面
顺序建议:
- 设置页(最多重复的输入/弹窗/分组)
- 侧栏与标题栏
- 自动化、资料库、项目
- 能力中心宿主壳
- 首页与输入区外壳
恢复的会话渲染、工具卡、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 先修,否则无法做渲染验收 |
建议的第一批交付
如果现在开始,建议第一批只做四件事:
design-system.md:现状 + 目标 token 表 + 组件清单 + 例外。- 根级
--wb-*桥接:先让语义色和主题归一。 Button/Input/IconButton三个基础组件。- 迁移设置页作为试点,跑浅色/深色/窄窗截图和回归。
这一批完成后,后续页面可以按同一模式复制,不需要再讨论架构。