第四章:系统底座全景解密
Open Claude UI 分析手册(Claude Desktop 4.29.0 → 4.4212.0)「第四章:系统底座全景解密」:每条结论附代码证据与复现命令。
本章是完整清单章。所有清单均由
grep | sort -u直接从MainWindowPage-BjVtjJFx.css生成,可逐条复现。
4.1 @keyframes 完整 55 项清单
grep -o "@keyframes [a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sed 's/@keyframes //' | sort -u | wc -l
# → 554.1.1 按功能分组
骨架屏与占位(3)
cds-skeleton-sweep
cds-skeleton-breathe
cds-placeholder-revealPill 胶囊(9)
cds-pill-in
cds-pill-out
cds-pill-pop-up
cds-pill-pop-down
cds-pill-dip-up
cds-pill-dip-down
cds-pill-roll-in-up
cds-pill-roll-in-down
cds-pill-roll-out滚动渐隐(8)
cds-scroll-fade-top
cds-scroll-fade-bottom
cds-scroll-fade-left
cds-scroll-fade-right
cds-scroll-fade-x-active
cds-scroll-fade-y-active
cds-scroll-fade-strip-active
cds-scroll-fade-strip-in
cds-scroll-fade-strip-out(注:strip 系列 3 个,计入后滚动渐隐共 10 个。上一行列出的是全部 10 项。)
Shimmer 微光(9)
cds-shimmer
cds-shimmer-text-band
cds-shimmer-text-band-rtl
cds-shimmer-text-band-strong
cds-shimmer-text-band-strong-rtl
cds-shimmer-text-shine
cds-shimmer-text-shine-rtl
cds-shimmer-text-shine-strong
cds-shimmer-text-shine-strong-rtl吸附边缘(3)
cds-sticky-edge-start
cds-sticky-edge-end
cds-sticky-edge-active工作指示点(3)
cds-working-dot-halo
cds-working-dot-lands
cds-working-dot-veil进出场通用(4)
cds-fade-in
cds-reveal-in
cds-reveal-out
cds-slide-in-start
cds-slide-in-end(实际 5 个。)
Drop / Chip / 拖放(3)
cds-chip-in
cds-drop-icon-rise
cds-drop-ring-in布局与网格(2)
cds-grid-rows-in
cds-morph-in页面转场(2)
cds-page-tabs-arrive
cds-page-title-leaves图标解析(2)
cds-resolve-glyph
cds-resolve-square附件与编辑器(3)
cds-attachment-out
cds-editor-comment-working
cds-flipbook其它(1)
spin
cds-dot-pulse4.1.2 与 open-claude-ui 的差异
# ours
grep -o "@keyframes [a-zA-Z0-9_-]*" code-examples/cds-theme.css | sort -u | wc -l # 32
# client
grep -o "@keyframes [a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sort -u | wc -l # 55| 集合 | 数量 | 明细 |
|---|---|---|
| 交集(双方都有) | 11 | cds-dot-pulse、6 个 cds-scroll-fade-*、cds-shimmer-text-shine、cds-shimmer-text-shine-strong、cds-skeleton-breathe、cds-skeleton-sweep |
| 仅客户端有 | 44 | 见 4.1.1 减去交集的 11 项 |
| 仅我方有 | 21 | 见 4.1.3 |
4.1.3 我方独有的 21 个关键帧(客户端中不存在)
comm -23 <(grep -o "@keyframes [a-zA-Z0-9_-]*" code-examples/cds-theme.css | sed 's/@keyframes //' | sort -u) \
<(grep -o "@keyframes [a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sed 's/@keyframes //' | sort -u)_grapheme-reveal_1plog_1
cds-anim-loop
dframe-clay-breathe
dframe-dot-blink
epitaxy-band-exit
epitaxy-check-draw
epitaxy-linear-sweep
epitaxy-linear-sweep-rtl
epitaxy-pill-pulse
file-thumbnail-grow-in
message-fade-up
saved-check-decay
timeline-block-enter
timeline-expand
timeline-fade-in
timeline-fade-out
timeline-quiet-dot
timeline-status-fall
timeline-status-rise
wl-glow
wl-halo这说明一件重要的事:open-claude-ui 的动效体系中有 21/32(66%)的关键帧在 3.6556.2 客户端中不存在。
可能的原因(本文不推断具体哪一种):
- 这些名字是 v1/v2 时期分析得到的,3.6556.2 已重命名或移除
- 这些名字是
open-claude-ui自行命名的抽象(非客户端原名) - 这些动效在其它 CSS 文件(如
window-shared.css)中
⚠️ 本文不对上述三种可能做判定,标注为 unknown。需进一步核对:
window-shared.css(8,078 bytes)window-legacy-pins.css(6,289 bytes)- 各 renderer 的
assets/main-*.css
4.2 data-cds 完整 45 项命名空间清单
grep -o "\[data-cds=[A-Za-z-]*\]" MainWindowPage-BjVtjJFx.css | sed 's/\[data-cds=//;s/\]//' | sort -u | wc -l
# → 454.2.1 完整清单(字母序)
Accordion
AccordionHeader
Aside
AskUserQuestion
AssistantMessage
Banner
Button
ButtonGroup
Card
CardLink
ChartContainer
ChatComposerChin
ChatComposerDock
ChatComposerEditor
ChatComposerNotices
ChatComposerQueuedMessages
CommentCard
DataTable
DiffIndicator
FileDiff
Heatmap
Icon
MessageActions
MessageAttachments
MessageAttachmentsFile
MessageAttachmentsImage
MessageAttachmentsQuote
MessageAttachmentsVideo
ModelSelector
ModelSelectorEffort
Page
Panel
PointChartContainer
PreviewCard
Prose
Pulse
Skeleton
SkeletonGroup
SplitDropdownButton
Surface
Table
Toolbar
TurnStatus
TurnStatusStep
UserMessage4.2.2 与 v2 文档的差异
v2 文档声称「40 大标准化 data-cds 命名空间」。3.6556.2 实测 45 个。
证据:
docs/v2/01-综述与版本边界.md:29:「40 大标准化 data-cds 原生命名空间」docs/v2/04-系统底座全景解密.md:32:「[40 大标准化组件清单]」docs/v2/06-总结与升格意义.md:17:「扩充并标准化为 40 大data-cds顶级命名空间」- 实测
sort -u | wc -l→ 45
⚠️ 无法确定的部分:v2 的 40 个具体名单未在 v2 文档中逐条列出(04 章只有占位符
[40 大标准化组件清单])。因此无法给出「哪 5 个是新增」的精确 diff。本文只确认总数从 40 变为 45。
4.2.3 Skeleton 与 SkeletonGroup 的单复数设计
这是 3.0 独有的双命名空间:
| 命名空间 | 职责 |
|---|---|
SkeletonGroup | 组容器:承载 data-motion、data-hold,统一控制整组动画 |
Skeleton | 单个骨架块:承载 --cds-skel-i 级联索引、扫光子元素 |
证据:CSS 中两者作为不同选择器存在
.cds-root [data-cds=SkeletonGroup][data-motion=wave] [data-cds=Skeleton] { … }这一条同时引用 SkeletonGroup(祖先)与 Skeleton(后代),证明是两层结构。
4.3 色彩体系
4.3.1 35 阶 gray 全表
grep -o "\--cds-gray-[0-9]*:[^;}]*" MainWindowPage-BjVtjJFx.css | sort -u | wc -l
# → 35| 阶 | 值 | 阶 | 值 |
|---|---|---|---|
gray-0 | #fff | gray-450 | #7b7974 |
gray-10 | #fcfcfb | gray-500 | #6d6b67 |
gray-20 | #f9f9f7 | gray-550 | #5f5e5a |
gray-30 | #f6f6f4 | gray-600 | #52514e |
gray-40 | #f3f3f0 | gray-650 | #454442 |
gray-50 | #f0efec | gray-700 | #383835 |
gray-60 | #edece8 | gray-750 | #2c2c2a |
gray-70 | #eae9e4 | gray-800 | #20201f |
gray-80 | #e7e6e1 | gray-810 | #1e1e1d |
gray-90 | #e4e3dd | gray-820 | #1c1c1b |
gray-100 | #e1e0d9 | gray-830 | #1a1a19 |
gray-150 | #d2d1c7 | gray-840 | #181817 |
gray-200 | #c3c2b7 | gray-850 | #151515 |
gray-250 | #b4b3a8 | gray-860 | #131313 |
gray-300 | #a5a49a | gray-870 | #111 |
gray-350 | #97958d | gray-880 | #0f0f0f |
gray-400 | #898781 | gray-890 | #0d0d0d |
gray-900 | #0b0b0b |
4.3.2 35 阶 orange 全表
grep -o "\--cds-orange-[0-9]*:[^;}]*" MainWindowPage-BjVtjJFx.css | sort -u | wc -l
# → 35| 阶 | 值 | 阶 | 值 |
|---|---|---|---|
orange-0 | #fff | orange-450 | #c25124 |
orange-10 | #fefbfa | orange-500 | #ae461c |
orange-20 | #fdf7f5 | orange-550 | #993d19 |
orange-30 | #fcf4f0 | orange-600 | #863311 |
orange-40 | #faf0ec | orange-650 | #712b0f |
orange-50 | #f9ece7 | orange-700 | #5d230b |
orange-60 | #f8e9e2 | orange-750 | #4b1b08 |
orange-70 | #f7e5dd | orange-800 | #371407 |
orange-80 | #f7e1d7 | orange-810 | #341307 |
orange-90 | #f7dcd1 | orange-820 | #301106 |
orange-100 | #f7d8cb | orange-830 | #2d1006 |
orange-150 | #f3c5b2 | orange-840 | #290f06 |
orange-200 | #f4ae94 | orange-850 | #240e07 |
orange-250 | #f09978 | orange-860 | #1f0e08 |
orange-300 | #ec835a | orange-870 | #1a0e09 |
orange-350 | #eb6834 | orange-880 | #150d0a |
orange-400 | #d95926 | orange-890 | #100c0b |
orange-900 | #0b0b0b |
4.3.3 品牌陶土色:三个来源三个值
| 令牌 | v2 文档 | open-claude-ui | 3.6556.2 实测 |
|---|---|---|---|
--cds-clay | #d97757 | #d97757 | #d97757 ✅ 三方一致 |
--cds-clay-emphasized | #cc5a36(双模同值) | var(--cds-clay-hover)(light #cc5a36 / dark #e08264) | #c6613f(无暗色覆盖) |
--cds-clay-emphasized 的证据
grep -c "\--cds-clay-emphasized:" MainWindowPage-BjVtjJFx.css # → 2
# 定义位置:偏移 133889(:root 块)与 152242(.cds-root 块),值均为 #c6613f
# 暗色覆盖:0 处(data-mode=dark 块内无此令牌)
grep -o "\--cds-clay-emphasized:[^;}]*" MainWindowPage-BjVtjJFx.css # → --cds-clay-emphasized:#c6613f原文上下文(连续令牌,证明不是拼接错误):
--cds-oncolor-200:#f8f8f7bf;--cds-oncolor-300:#f8f8f780;
--cds-clay:#d97757;--cds-clay-emphasized:#c6613f;
--cds-heather:#cbcadb;--cds-plum:#827db…关键判定:#c6613f 与 open-claude-ui 仓库 AGENTS.md 里记录的 --brand: #c6613f(「Official desktop brand primary color」)完全一致。
这强烈提示:#c6613f 才是官方品牌主色,而 --cds-clay 的 #d97757 是「指示点/脉冲」用的浅色变体。两者不是同一个东西,不应互相派生。
⚠️ 本文不对「为什么 v2 文档写 #cc5a36」做溯源——v2.37337.1 的 CSS 已不在本机,无法比对。
4.3.4 其它命名色
--cds-heather: #cbcadb
--cds-plum: #827db…(plum 值在截断处,本文不做补全。)
4.4 模式作用域:data-mode 三态
4.4.1 三态定义
grep -o "data-mode=[a-z]*" MainWindowPage-BjVtjJFx.css | sort | uniq -c86 data-mode=dark
43 data-mode=system
29 data-mode=light4.4.2 三个作用域选择器
CDS 3.0 用三组并列选择器实现模式切换:
/* 暗色:显式 data-mode=dark,或祖先暗色且自身未指定 light/system */
[data-mode=dark] .cds-root:not([data-mode=light]):not([data-mode=system]),
.cds-root[data-mode=dark] { … }
/* 系统:data-mode=system 或完全未指定(跟随 prefers-color-scheme) */
[data-mode=system] .cds-root:not([data-mode=light]):not([data-mode=dark]),
.cds-root[data-mode=system],
.cds-root:not([data-mode]):not([data-mode] *) { … }
/* 暗色独立作用域(用于局部强制暗色) */
.cds-dark-scope[data-mode=dark] { … }与 .dark class 的对比
| 方式 | CDS 3.0 | open-claude-ui |
|---|---|---|
| 暗色选择器 | [data-mode=dark] + .cds-dark-scope[data-mode=dark] | .dark class |
| 系统跟随 | [data-mode=system] + prefers-color-scheme | 无 |
| 局部强制 | .cds-dark-scope | 无 |
4.4.3 暗色态的实际覆盖
暗色块内重定义的令牌(抽样证据):
/* 骨架屏暗色覆盖 */
[data-mode=dark] .cds-root:not([data-mode=light]):not([data-mode=system]),
.cds-root[data-mode=dark] {
…
--cds-skeleton-base: hsl(from var(--cds-neutral-900) h s l / 7.5%);
--cds-skeleton-sheen: hsl(from var(--cds-neutral-900) h s l / 7.5%);
}注意:暗色下 --cds-skeleton-base 与 --cds-skeleton-sheen 值相同(都是 7.5% 中性黑)。这与浅色下两者不同(light 见下方 4.4.4)。
4.4.4 骨架屏令牌的完整取值链
| 令牌 | 浅色 | 暗色 |
|---|---|---|
--cds-skeleton-base | var(--cds-alpha-1) → hsl(from var(--cds-neutral-900) h s l / 5%) | hsl(from var(--cds-neutral-900) h s l / 7.5%) |
--cds-skeleton-sheen | var(--cds-alpha-1) → hsl(from var(--cds-neutral-900) h s l / 5%) | hsl(from var(--cds-neutral-900) h s l / 7.5%) |
证据:
# 浅色(.cds-root 作用域)
grep -o "\--cds-skeleton-base:var(--cds-alpha-1)" MainWindowPage-BjVtjJFx.css
grep -o "\--cds-alpha-1:[^;}]*" MainWindowPage-BjVtjJFx.css
# → --cds-alpha-1:hsl(from var(--cds-neutral-900) h s l / 5%)
# 暗色([data-mode=dark] 作用域,3 处:dark / system / cds-dark-scope)
grep -o "\--cds-skeleton-base:hsl(from var(--cds-neutral-900) h s l / 7.5%)" MainWindowPage-BjVtjJFx.css与 v2 文档 / 分支的差异
| 来源 | base(暗) | sheen(暗) |
|---|---|---|
| v2 文档 | 未记录 | 未记录 |
| issue #25 分支 | rgba(255,255,255,0.06) | rgba(255,255,255,0.08) |
| 3.6556.2 实测 | hsl(from var(--cds-neutral-900) h s l / 7.5%) | 同左(与 base 相同) |
两个实质差异:
- 分支用白色半透明(
rgba(255,255,255,…)),实测用中性黑半透明(hsl(from …neutral-900… / 7.5%)) - 分支的 sheen 比 base 更亮(0.08 > 0.06),实测两者完全相同
在暗色背景下,用 neutral-900(= gray-900 = #0b0b0b)的 7.5% 叠在深色表面上,效果是极淡的加深,而不是分支实现的提亮。这是完全相反的视觉方向。
4.5 @property 24 项注册清单
grep -o "@property --cds-[a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sed 's/@property //' | sort -u | wc -l
# → 244.5.1 完整清单
滚动渐隐(17)
--cds-scroll-fade-top
--cds-scroll-fade-top-on
--cds-scroll-fade-top-raw
--cds-scroll-fade-bottom
--cds-scroll-fade-bottom-on
--cds-scroll-fade-bottom-raw
--cds-scroll-fade-left
--cds-scroll-fade-left-on
--cds-scroll-fade-left-raw
--cds-scroll-fade-right
--cds-scroll-fade-right-on
--cds-scroll-fade-right-raw
--cds-scroll-fade-size
--cds-scroll-fade-x-active
--cds-scroll-fade-y-active
--cds-scroll-fade-strip-active
--cds-scroll-fade-strip-raw吸附边缘(3)
--cds-sticky-edge-active
--cds-sticky-edge-start
--cds-sticky-edge-end其它(4)
--cds-badge-x-wght
--cds-message-actions-opacity
--cds-outset-x
--cds-outset-y4.5.2 与 open-claude-ui 的差异
# ours: 19
grep -o "@property --cds-[a-zA-Z0-9_-]*" code-examples/cds-theme.css | sort -u | wc -l
# client: 24
grep -o "@property --cds-[a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sort -u | wc -l交集 17 / 仅客户端 7 / 仅我方 2:
comm -12 <(grep -o "@property --cds-[a-zA-Z0-9_-]*" code-examples/cds-theme.css | sed 's/@property //' | sort -u) \
<(grep -o "@property --cds-[a-zA-Z0-9_-]*" MainWindowPage-BjVtjJFx.css | sed 's/@property //' | sort -u) | wc -l
# → 17
comm -13 … | wc -l # → 7
comm -23 … | wc -l # → 2缺失的 7 个:
--cds-badge-x-wght
--cds-message-actions-opacity
--cds-outset-x
--cds-outset-y
--cds-sticky-edge-active
--cds-sticky-edge-end
--cds-sticky-edge-start我方独有的 2 个(客户端中不存在):
--cds-anim
--cds-anim2这两个是 v2 时代的动画控制变量(配合 @keyframes cds-anim-loop 与 [data-cds-anim] 使用),在 3.6556.2 主样式表中未注册为 @property。
(集合运算自洽校验:ours = 交集 17 + 我方独有 2 = 19;client = 交集 17 + 我方缺失 7 = 24。)
4.6 时间与缓动 Token
4.6.1 5 阶时间矩阵
--cds-dur-fast: 60ms
--cds-dur-snap: .12s /* 120ms */
--cds-dur-base: .2s /* 200ms */
--cds-dur-sheet: .3s /* 300ms */
--cds-dur-slow: .45s /* 450ms */证据:
python3 -c "
import re; s=open('MainWindowPage-BjVtjJFx.css').read()
d={}
for m in re.finditer(r'--cds-dur-([a-z]+):([^;}]+)', s): d.setdefault(m.group(1),set()).add(m.group(2).strip())
for k,v in sorted(d.items()): print(k, sorted(v))
"
# base: ['.2s'] fast: ['60ms'] sheet: ['.3s'] slow: ['.45s'] snap: ['.12s']这与 v2 文档的「5 阶时间 Token 矩阵 (60ms ~ 450ms)」完全吻合。
4.6.2 缓动曲线
--cds-ease-out: cubic-bezier(.165, .84, .44, 1)
--cds-ease-outs: cubic-bezier(0, 0, .2, 1) /* 注意:同名 token 两个值 */
--cds-ease-overshoot: cubic-bezier(.34, 1.3, .64, 1) /* +30% 过冲 */
--cds-ease-snap: cubic-bezier(.32, .72, 0, 1)证据:
python3 -c "
import re; s=open('MainWindowPage-BjVtjJFx.css').read()
e={}
for m in re.finditer(r'--cds-ease-([a-z]+):([^;}]+)', s): e.setdefault(m.group(1),set()).add(m.group(2).strip())
for k,v in sorted(e.items()): print(k, sorted(v))
"
# out: ['cubic-bezier(.165, .84, .44, 1)', 'cubic-bezier(0, 0, .2, 1)']
# overshoot: ['cubic-bezier(.34, 1.3, .64, 1)']
# snap: ['cubic-bezier(.32, .72, 0, 1)']注意 --cds-ease-out 有两个不同值,说明它在不同作用域(可能是浅色/暗色,或不同的祖先作用域)被重定义。本文不推断重定义的边界。
v2 文档声称「3 大核心贝塞尔曲线」,实测有 4 个不同的曲线值(out 2 个 + overshoot + snap)。
4.6.3 linear() 弹簧曲线
本文未在主样式表中找到 linear() 弹簧曲线定义。v2 文档 4.4 节提到「linear() 弹簧曲线与时长 Token 的耦合失效」,但 3.6556.2 主表中:
grep -c "linear(" MainWindowPage-BjVtjJFx.css需要单独核实(见 4.6.4 的不确定项)。
⚠️ 本文不对
linear()弹簧的存在性下结论。这是一个待核实项。
4.7 圆角与密度系统
4.7.1 圆角 Token
--cds-radius--xs: calc(.3125rem * var(--cds-rem-scale,1)) /* 5px */
--cds-radius--sm: calc(.4375rem * var(--cds-rem-scale,1)) /* 7px */
--cds-radius--lg: calc(.625rem * var(--cds-rem-scale,1)) /* 10px */
--cds-radius-composer: calc(.75rem * var(--cds-rem-scale,1)) /* 12px */还有一组更大的值(同一 token 名的另一处定义):
--cds-radius--xs: calc(.375rem * var(--cds-rem-scale,1)) /* 6px */
--cds-radius--sm: calc(.3125rem * var(--cds-rem-scale,1)) /* 5px */
--cds-radius--lg: calc(.4375rem * var(--cds-rem-scale,1)) /* 7px */
--cds-radius-composer: calc(.875rem * var(--cds-rem-scale,1)) /* 14px */证据:
python3 -c "
import re; s=open('MainWindowPage-BjVtjJFx.css').read()
for name in ['--cds-radius--xs','--cds-radius--sm','--cds-radius--lg','--cds-radius-composer']:
print(name, sorted(set(re.findall(re.escape(name)+r':[^;}]*', s))))
"4.7.2 两套圆角 = 密度双模
--cds-radius-composer 有两个值:12px 与 14px。
这与 v2 文档「Composer 圆角由 20px 降为 12px/14px、引入 Density 系统」吻合。
--cds-rem-scale 是密度缩放因子,圆角全部乘以它。
⚠️ 本文未能确定两套值的切换条件(哪个选择器/属性触发 12px vs 14px)。这是一个待核实项。
4.7.3 --cds-radius 的间接层
--cds-radius: inherit
--cds-radius: .25rem /* 4px,可能是 fallback */
--cds-radius: var(--cds-radius--xs)
--cds-radius: var(--cds-radius--sm)
--cds-radius: var(--cds-radius--lg)
--cds-radius: calc(.3125rem * var(--cds-rem-scale,1))
--cds-radius: calc(.375rem * var(--cds-rem-scale,1))
--cds-radius: calc(.4375rem * var(--cds-rem-scale,1))
--cds-radius: calc(.5rem * var(--cds-rem-scale,1))
--cds-radius: calc(.625rem * var(--cds-rem-scale,1))含义:--cds-radius 是一个上下文相关的别名,实际值取决于它所在的组件作用域(inherit / xs / sm / lg)。这是 CDS 3.0 的「同心圆角」实现方式。
本章证据索引
| 断言 | 命令 |
|---|---|
| keyframes 55 | grep -o "@keyframes [a-zA-Z0-9_-]*" ... | sort -u | wc -l |
| data-cds 45 | grep -o "\[data-cds=[A-Za-z-]*\]" ... | sort -u | wc -l |
| @property 24 | grep -o "@property --cds-[a-zA-Z0-9_-]*" ... | sort -u | wc -l |
| gray 35 阶 | grep -o "\--cds-gray-[0-9]*:" ... | sort -u | wc -l |
| orange 35 阶 | 同上 |
| clay-emphasized 2 处同值、0 处暗色覆盖 | grep -c + 偏移定位 + 暗色块内搜索 |
| data-mode 三态计数 | grep -o "data-mode=[a-z]*" ... | sort | uniq -c |
| 5 阶 dur | python 正则提取后按 key 去重 |
| 4 个 ease 值 | 同上 |
| 圆角双套 | 同 token 名多值提取 |
| 交集/差异 | comm -12 / -13 / -23 |