Design tokens v2
设计 Token v2
This page is only available in Chinese. It is shown in its original language.
范围
Token 源改为 DTCG 2025.10 JSON,经 Style Dictionary v5 生成现有的 TS Theme 与 --klc-* CSS 变量;在 Theme 之外新增一层与框架无关的基础 Token(圆角、字阶、间距、密度、阴影层级、动效、层级、断点、品牌强调色)。
- Theme 层(
lightTheme/darkTheme/ 五种预设)的取值不变:生成结果与迁移前逐字节一致,baseline.test.ts.snap未改动。 - 渲染器不受影响:
engine/renderers、engine/scale、controllers、fonts.ts无改动(ADR 0005、0008)。 - 本阶段不迁移组件(Phase 2),也不应用重新生成的预设色板(Phase 3)。
源与构建
packages/core/src/foundation/tokens/dtcg/
kcq.resolver.json DTCG Resolver Module 2025.10:sets + modifiers(preset × mode)
foundation/*.tokens.json 与 mode、preset 无关:旧 Theme 族 + v2 基础 Token
scheme/{light,dark}.tokens.json Pro 基底,按 light.* / dark.* 分支并存
preset/*.tokens.json 预设覆盖;visual-mapping 对应 createVisualTheme 的映射
mode/{light,dark}.tokens.json 生成的选择器:把选中分支别名到公开名称(勿手改)解析顺序为 foundation → scheme → preset → mode。别名在全部源合并后统一解析(Resolver 规范要求),所以预设可以同时覆盖两种 mode,而两个 modifier 互不依赖。
Style Dictionary 尚未支持 Resolver(style-dictionary#1590),由 packages/core/scripts/tokens/build.mjs 枚举 10 个排列,每个排列交给 Style Dictionary 做别名解析与 DTCG → CSS 值转换。
| 命令 | 作用 |
|---|---|
pnpm tokens:build | 写入全部生成文件 |
pnpm tokens:check | 只校验;与磁盘不一致时退出码 1(pipeline.test.ts 也会执行) |
生成物:
| 文件 | 内容 |
|---|---|
theme-base.ts、interface-colors.ts、theme-light.ts、theme-dark.ts、presets/impl/visualPalettes.ts | 既有 Theme 源文件,现由 DTCG 生成 |
foundation.ts | v2 基础 Token(lightFoundation / darkFoundation / foundationTokens) |
packages/core/design-tokens/css/theme.<preset>.<mode>.css | 每个排列的 Theme 层 --klc-*,与运行时 themeToCssVars 逐字节一致 |
packages/core/design-tokens/css/foundation.css | 基础 Token::root + [data-theme='light'/'dark'] |
packages/core/design-tokens/dtcg/kcq.bundle.resolver.json | 内联全部源的单文件解析器,供 nebutra-sailor 等外部消费 |
rgba()、hsl()、transparent 这类无法由 hex 还原的原文写入 $extensions["io.github.363045841.kcq"].css,保证逐字节输出。em 字距用 number + $extensions[…].unit = "em" 表达(DTCG dimension 只允许 px/rem);tabular-nums 这类 CSS 关键字放在分组的 $extensions[…].keywords(DTCG 没有关键字类型)。
消费
- Vue / 运行时:
resolveTheme(mode, …)返回的 Theme 带foundation;useChartTheme经themeToCssVars把它们与颜色一起挂到图表根节点。lightTheme/darkTheme本身不带foundation,输出保持冻结基线。 - 静态 CSS:引入
design-tokens/css/foundation.css;明暗块按data-theme选择(与KLineChart.vue根节点一致)。 - TS:
import { foundationTokens } from '@363045841yyt/klinechart-core',类型FoundationTokens(Theme.foundation为可选字段,手写 Theme 无需补齐)。
命名规则:路径逐段 kebab 化后用 - 连接,radius.sm → --klc-radius-sm,text.label.13.fontSize → --klc-text-label-13-font-size。
Token 清单
每个取值的依据写在 DTCG $description 中;研究未直接给出的值标注 [derived] 并说明推导。
| 组 | 变量 | 取值 | 依据 |
|---|---|---|---|
| 圆角 | --klc-radius-{0,xs,sm,md,lg,xl,full} | 0 / 4 / 6 / 8 / 12 / 16 / 9999px | craft-principles §4.1;xs 为同心推导 6−2 [derived] |
| 字阶 | --klc-text-{12,13,14,16,20,24,32,48,72}-{font-size,line-height,letter-spacing,font-weight} | 见 §4.2 表;12–16 行高 [derived] | §4.2;10px 移除 |
| 等宽 | --klc-text-11-mono-* | 11/16,仅限等宽/表格数字 | §4.2、design §3.3 |
| 角色 | --klc-text-label-{12,13,14,16,20}-*、--klc-text-copy-{13,14,16,20,24}-* | 引用字阶;label ≤16 字重 500 [derived] | CP 18(Geist Label / Copy) |
| 数字 | --klc-text-numeric | tabular-nums | CP 19、ADR 0004 |
| 间距 | --klc-space-{2…160} | Carbon 2–160 | §4.3(S28) |
| 密度 | --klc-density-{compact,default,comfortable,touch,hit-target,hit-target-touch} | 24 / 32 / 40 / 48 / 24 / 44 | §4.3、CP 27 |
| 阴影 | --klc-elevation-{1,2,3,hairline} | 浅色:阴影 + hairline;深色:表面提亮为主、软阴影 [derived 透明度] | §4.4 |
| 动效 | --klc-motion-ease-{out,in-out,drawer,expo}、--klc-motion-dur-{0,press,fast,base,slow,sheet,toast}、--klc-motion-{stagger,loader-delay,loader-min} | §4.5;slow 260 [derived] | §4.5 |
| 行情闪烁 | --klc-motion-flash / --klc-motion-fade | 500ms / 1000ms,减少动效时关闭 | design §3.3 |
| 层级 | --klc-z-index-{base,chart-overlay,sticky,dropdown,popover,modal,toast,tooltip} | 0 / 10 / 100 / 1000 / 1010 / 1100 / 1150 / 1200 | 顺序来自 design §3.3;数值锚定现有用法 [derived] |
| 断点 | --klc-breakpoint-{sm,md,lg,xl} | 640 / 768 / 1024 / 1280 | design §3.3(项目自定) |
| 品牌 | --klc-brand-accent、--klc-brand-accent-text | #4C77C6;文字 #537ECD(深)/ #456FBD(浅) | ADR 0007 |
旧的 --klc-spacing-*、--klc-typography-*、--klc-motion-duration-* / -easing-* 保留原值;新动效使用 dur-* / ease-*,名称不冲突。
调色板生成器(仅提案)
pnpm tokens:palettes 读取每个 preset × mode 当前解析出的输入,在 OKLCH 中生成 Radix 12 级语义阶梯(CP 1、CP 4;culori),写入 design-tokens/proposals/palettes.{json,md}。运行时主题不读取这些文件,Canvas 颜色不变;接入属于 Phase 3。
- 输入:
base(color.ui.background)、accent(color.ui.accent)、contrast(默认 1)、neutralTint(accent|pure,design §4 契约)。另以品牌色、涨跌色、warning、danger 为锚点生成彩色阶梯。 - 轮廓:明度 / 彩度轮廓取自
@radix-ui/[email protected]的 gray / blue(明暗各一套,值内置于scripts/tokens/lib/radix-reference.mjs)。step 1 = base,step 9 = 锚点,11/12 外推到 step 2 上 APCA Lc 60 / 90(Radix 的保证)且 WCAG ≥ 4.5:1。插值方式与neutralTint提案规则为 [derived],记录在palettes.json的method中。 - 报告附每个提案的 WCAG / APCA 检查。当前唯一未通过的是作为输入的浅色 warning
#C58A1A(step 9 在 2.67–2.86:1),留给 Phase 3。
对比度门槛
contrastGate.test.ts(随 core 的pnpm test运行):对 5 个预设 × 明暗 × 涨跌约定(绿涨 / 红涨)共 20 个组合、每个组合 27 组声明的前景 / 背景对执行 WCAG 2 AA:文字 4.5:1,组件与图形 3:1。半透明颜色先按浏览器的方式与底色合成再计算。- 当前色板已有不达标项,记录在
__tests__/contrast-known-failures.json;测试只在出现新的失败或已知失败进一步变差时报错。涨跌颜色不允许进入已知清单。 pnpm tokens:contrast生成design-tokens/reports/contrast.{md,json},其中 APCA 仅作建议(文字 |Lc| ≥ 60、组件 ≥ 45,约等于 4.5:1 / 3:1)。APCA 仍是 beta 且许可证非 OSI,因此不作为门槛;apca-w3依赖 AGPL 的colorparsley,所以改用 colorjs.io(MIT)的 APCA 实现。- 只有在评审接受后才运行
pnpm tokens:contrast --update-known重写已知清单。
不变量与测试
pipeline.test.ts:生成文件与源同步;每个 preset × mode 的 Theme CSS 与themeToCssVars(resolveTheme(…))逐字节一致,DTCG 中的预设映射与createVisualTheme因此不会各自漂移;foundation.css与运行时 foundation 变量一致;调色板提案与源同步。foundation.test.ts:锁定研究取值,并校验字距随字号收紧、同心圆角、层级单调、两种 mode 键集一致、品牌色在 Pro 表面满足 AA。contrastGate.test.ts:见上节。baseline.test.ts.snap:Theme 层快照,保持不变。
后续
- Phase 2:组件改用
--klc-*,stylelint 禁止原始值。 - Phase 3:
PresetPersonality(预设的圆角 / 密度 / 阴影个性)与重新生成的色板接入,清空已知对比度失败。