首页
/ Claude Code Dataviz 参考调色板:一份可验证的数据可视化默认色板及其背后的计算式校验

Claude Code Dataviz 参考调色板:一份可验证的数据可视化默认色板及其背后的计算式校验

2026-09-04 13:17:24作者:翟萌耘Ralph

本文以 Claude Code 的 dataviz skill 中的参考调色板文件(references/palette.md)为主体,完整讲解这份"设计系统无关"默认色板的全部参数:分类色 8 槽位的明暗双模式取值、顺序/发散色阶、固定状态色、图表墨水与界面色、纹理填充通道,并对照仓库中 validate_palette.js / validate_palette.py 的阈值常量与校验逻辑,说明这些数值是如何被计算验证的,以及替换为品牌色后如何复验。读完后你可以直接复制这份色板到 HTML/SVG 图表中,并用同一套校验脚本对任意自定义色板做六项可计算检查。

参考调色板是什么:方法的"填好参数的实例"

palette.md 的定位非常明确:它是整个数据可视化方法的参考实例(reference instance)——方法所需的每一个参数,都被填入了一套经过验证的默认值。方法本身是品牌无关(brand-neutral)的:要适配自己的品牌,只需替换本文件的取值并重跑校验器,方法的其他部分一律不变。这一点在 SKILL.md 中同样强调:方法不变,设计系统提供一组参数(色阶、分类顺序、发散对、状态色、纹理、表面色、筛选控件),参考实例就是"每行都填好的那张参数表"。

所有取值都是纯 hex。文档给出的落地方式是:在 HTML 图表顶部的局部 <style> 块中,把你用到的槽位定义成 CSS 自定义属性,正文按"角色"引用而不是按裸 hex 引用——这样明暗两套值只在一处切换,图表主体始终对着角色书写:

.viz-root {
  color-scheme: light;
  --surface-1:      #fcfcfb;   /* chart surface */
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --series-1:       #2a78d6;   /* categorical slot 1 */
  /* ...only the roles this chart uses */
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .viz-root {
    color-scheme: dark;
    --surface-1:      #1a1a19;
    --text-primary:   #ffffff;
    --text-secondary: #c3c2b7;
    --series-1:       #3987e5;
  }
}
:root[data-theme="dark"] .viz-root {
  color-scheme: dark;
  --surface-1:      #1a1a19;
  --text-primary:   #ffffff;
  --text-secondary: #c3c2b7;
  --series-1:       #3987e5;
}

文档要求暗色值同时声明在上述两个作用域下:媒体查询覆盖操作系统设置,data-theme 作用域覆盖页面内的主题切换按钮,且两者必须双向生效——:not([data-theme="light"]) 守卫让"页面显式设为浅色"能压过"系统暗色",:where() 则保证媒体查询块的优先级低于切换作用域。这是该文件中一个容易踩坑的实现细节:少写任何一个作用域,暗色切换都会出现一种"赢不了"的场景。

分类调色板:8 个槽位、明暗双模式,以及顺序即安全机制

分类色板共 8 个槽位。暗色列不是另一套色板,而是同样的 8 个色相针对暗色表面重新步进(re-step)的结果:

Slot 色相 Light Dark
1 blue #2a78d6 #3987e5
2 orange #eb6834 #d95926
3 aqua #1baf7a #199e70
4 yellow #eda100 #c98500
5 magenta #e87ba4 #d55181
6 green #008300 #008300
7 violet #4a3aa7 #9085e9
8 red #e34948 #e66767

这组顺序在两种模式下都通过了全部硬性门槛(默认 adjacent 配对清单,适用于堆叠、条形、折线):

  • 相邻 CVD Delta E(OKLab ×100)最差值:9.1(light)/ 8.4(dark),目标 ≥ 8;
  • 相邻正常视觉 Delta E 最差值:19.6(light)/ 19.3(dark),地板 ≥ 15。

关键理解是:槽位顺序本身就是 CVD 安全机制,不是装饰。候选顺序被枚举过,只有两种模式下都通过每一道相邻门槛的顺序被保留,这套默认值只是合格顺序之一,因"开头颜色更和谐"被选中(详见 color-formula.md 的 Themes 一节)。当你换入自己品牌的色相时,也必须做同样的事:对候选顺序跑校验器,只在通过者中选择。

文件还记录了 2026 年 7 月的一次重排历史,值得了解它说明的规则边界:

  • 重排前后是同样的 8 个色相与步进,零 hex 变更,只是顺序变了;
  • 前一代顺序能通过"前 4 槽全对(all-pairs)"验证,但其暗色序列落在 CVD 6–8 的告警带,需要二级编码补偿;
  • 当前顺序主动放弃了第 4 槽(yellow 现在与 orange 相邻),换取更顺眼的开头色。

由此引出 --pairs all(散点、气泡、分级统计图、small multiples 等任意两标记可能并排的图表形式)下的系列数上限:完整 8 色在 all-pairs 下不可能过地板(28 个对子不再依赖顺序,而重步进又受"只用已文档化色板"规则限制),因此这些图表形式携带一个系列上限——前 3 个槽位在两种模式下通过全部对子验证(最差对 CVD Delta E 9.2 light / 9.4 dark,正常视觉 24.0 light / 20.9 dark,都高于 CVD 告警带)。超过 3 个系列就必须折叠进 "Other" 或改用 facet:第 4 槽会把 yellow 和 orange 同时放上屏幕,而这一对过不了 all-pairs 地板(正常视觉 13.7 light;CVD 4.8 dark)。

另一条硬性规定是救济规则(relief rule):light 模式下 magenta、yellow、aqua 三个槽位在浅色表面上的对比度低于 3:1,此时必须随图表提供可见的直接标签或表格视图。这条规则在 SKILL.md 的"Non-negotiables"中同样被列为不可豁免项。暗色步进则是按暗色带(OKLCH L ≈ 0.48–0.67,在暗色表面上 ≥ 3:1)选取并作为整体验证的。

顺序色(Sequential)与序级色(Ordinal):默认蓝色,一次只许一种色相

默认单一色相是蓝色,方向 light→dark。当同一页面同时出现两个顺序色上下文时,第二个上下文取下一个分类槽位的色相(orange),各自仍是独立的一色相色阶。完整 100→700 步进如下:

step hex step hex step hex step hex
100 #cde2fb 250 #86b6ef 400 #3987e5 550 #1c5cab
150 #b7d3f6 300 #6da7ec 450 #2a78d6 600 #184f95
200 #9ec5f4 350 #5598e7 500 #256abf 650 #104281
700 #0d366b

文档区分了两种用途,这个区分直接决定允许使用的范围:

  • Sequential 编码(连续量级——热力图、分级统计图):完整 100→700 范围都可用,最浅步进表示"接近零",允许向表面色退隐;
  • Ordinal 色阶(离散有序标记——漏斗阶段、层级,用 --ordinal 验证):离表面最近的那步进仍须过 2:1 对比。light 下不浅于 step 250#86b6ef,实测 2.06:1);dark 下不深于 step 600#184f95,实测 2.15:1)。

从源码可以印证 ordinal 模式的完整判定集:validate_palette.pyvalidate_palette.jsvalidate_ordinal / validateOrdinal 函数共做四项检查——亮度单调(按 L 排序须与输入顺序或其逆一致)、相邻步进 ΔL ≥ ORDINAL_MIN_DL = 0.06、最浅步进对表面 ≥ ORDINAL_LIGHT_FLOOR = 2.0、单一色相(OKLab 色相展布 ≤ 40°)。文档特别指出:把顺序色阶拿去跑分类六项检查会按设计 FAIL(它横跨亮度带、浅步进会跌破色度地板),这是预期行为而非真实失败,不要为了"修好"而破坏一条好色阶。

发散色对:蓝 ↔ 红,中性中点为灰

发散编码(极性)默认取 blue ↔ red——一暖一冷的两极,读起来天然像"对立面"。中性中点是灰色:light #f0efec、dark #383835,两臂步数相等。文档还记录了被否决的备选:blue↔aqua 因两端都偏冷、中点读不出"零/无"而被拒绝。这也呼应 SKILL.md 的不可协商项:发散色永远是两色相加中性灰中点,永不使用彩虹、也永不在中点放一个色相。

状态色板:固定四档,永不随主题变化

状态色(good/warning/serious/critical)是固定的、从不主题化的,独立于分类色存在:

role hex light 表面对比 dark 表面对比
good #0ca30c 3.27 5.19
warning #fab219 1.79 9.49
serious #ec835a 2.57 6.60
critical #d03b3b 4.68 3.62

设计上分三层考虑:

  1. 暗色下同四步进:全部 ≥ 3:1 于暗色表面 #1a1a19,且与暗色分类槽位保持可区分;
  2. 浅色下 warning 与 serious 故意低于 3:1:缓解手段是图标 + 标签的强制配对,所以状态色从不单独承载语义;
  3. 与分类槽位"恰好不同":按系列地板自己的尺子(未模拟 Delta E ≥ 15)量,每种模式下约有 9 组"分类 vs 状态"对低于 15——light 下 red↔critical 与 yellow↔warning 都只有 4.8,slot-2 orange 距 status-serious 5.8,light 的成功文字绿 #006300 距系列绿 10.1;green↔status-good 为 9.7,因两个 hex 均不随模式变化,两种模式都保持。也就是说区分度"够用但不够靠色相",通用规则是:任何系列色与同色相族的状态/delta 提示并排时,依赖图标 + 标签与摆放位置,永不依赖色相本身

纹理填充:可访问性专用通道

纹理通道只用一种手绘 "Lines" 填充,且只允许 45° 及其 135° 镜像两个方向,用填充自身色阶中更深的一档做 tone-on-tone 着墨。在量级色阶上它是有序的:旋转角度随数值步进,臂的角度携带发散的正负号。触发条件只有三类——辅助功能设置开启、打印、forced-colors;它永不做装饰、默认永不开启。

校验器:这些数值是如何算出来的

文档给出了校验器的两个内建默认表面色:

  • Light 图表表面:#fcfcfb
  • Dark 图表表面:#1a1a19

换入自己的色板时必须对着自己的表面重跑--surface <your-light> --mode light--surface <your-dark> --mode dark——对比度和亮度带结果只有相对于图表真实渲染的表面才有意义。

对照仓库源码,参考色板的每一项声称都能落到具体的阈值常量上。validate_palette.js(与 Python 孪生 validate_palette.py 保持同步)中:

const BAND = { light: [0.43, 0.77], dark: [0.48, 0.67] }; // OKLCH L
const CHROMA_FLOOR = 0.10; // OKLCH C
const CVD_TARGET = 8.0, CVD_FLOOR = 6.0; // OKLab ΔE×100, min(protan, deutan)
const NORMAL_FLOOR = 15.0; // 未模拟视觉,当前配对清单上的最差对
const CONTRAST_MIN = 3.0; // WCAG vs surface
const DEFAULT_SURFACE = { light: "#fcfcfb", dark: "#1a1a19" };

CVD 模拟采用的是 Machado, Oliveira & Fernandes (2009) 严重度 1.0 的色盲变换矩阵(validate_palette.js 中的 MACHADO 常量,覆盖 protan/deutan/tritan 三矩阵)。源码注释强调:该模拟模型是标准的一部分而非实现细节——换成例如 Viénot-1999 会使边缘对子的测量值移动,需要重新校准这些阈值。

实际运行 Python 孪生版校验参考色板 light 模式,输出与文档声称一致:

$ python3 Anthropic/claude-code/skills/dataviz/scripts/validate_palette.py \
    "#2a78d6,#eb6834,#1baf7a,#eda100,#e87ba4,#008300,#4a3aa7,#e34948" --mode light

Palette (light, surface #fcfcfb, categorical): 8 slots
  [PASS] Lightness band         all 8 inside L 0.43–0.77
  [PASS] Chroma floor           all 8 >= 0.1
  [PASS] CVD separation         worst adjacent #eda100↔#1baf7a ΔE 9.1 (protan) · tritan 5.8
  [PASS] Normal-vision floor    worst adjacent #e87ba4↔#eda100 ΔE 19.6 (normal)
  [WARN] Contrast vs surface    below 3:1 — relief required (visible labels or table view):
                                 [["#1baf7a",2.74],["#eda100",2.11],["#e87ba4",2.62]]

  → ALL CHECKS PASS

这正是文档所说的"最坏相邻 CVD ΔE 9.1 light""正常视觉 19.6 light"以及"三个浅色槽位低于 3:1 触发救济规则"的出处。运行注意事项:

  • JS 版是 ES moduleexport const contrast),直接 node validate_palette.js 在 CJS 环境会报语法错误;按文件名约定它会作为 CLI 自执行,也可在页面中以 <script type="module"> 引入——此时它读取 <body> 上的 data-palette(以及 data-mode / data-surface / data-pairs / data-ordinal)并用 console.table 输出报告;
  • Python 版可直接 python3 validate_palette.py "<hex,...>" --mode light|dark [--surface #hex] [--pairs adjacent|all] [--ordinal]
  • 退出码:无硬 FAIL 即 0;WARN 带(CVD 6–8 地板带、低于 3:1 的救济带)仍退出 0,但各自只允许在强制二级编码(直接标签、间距或纹理)下合法;正常视觉地板 < 15 是硬门槛,二级编码不能豁免它。

图表墨水与界面色(Chart chrome & ink)

除数据色外,参考实例还完整给出了图表"骨架"的明暗取值:

Role Light Dark
Chart surface #fcfcfb #1a1a19
Page plane #f9f9f7 #0d0d0d
Primary ink #0b0b0b #ffffff
Secondary ink #52514e #c3c2b7
Muted (axis/labels) #898781 #898781
Gridline (hairline) #e1e0d9 #2c2c2a
Baseline / axis #c3c2b7 #383835
Delta up good (success text) #006300 #0ca30c
Border (hairline ring) rgba(11,11,11,0.10) rgba(255,255,255,0.10)

筛选控件与字体:参考实例的行为规格

筛选控件属于标准 UI 而非图表组件——dataviz 只补充组合规则(详见 interaction.md)。参考规格为:日期范围控件是预设行的列表(today、last 7/30/90 days、month-to-date),选中用 16px 加粗对勾标记,hover 为 ghost wash,自定义范围放在页脚的一条 hairline 之后;维度筛选是标准 combobox。

字体与数字:全部内容——包括 hero 数字——都留在系统无衬线栈:system-ui, -apple-system, "Segoe UI", sans-serif,任何位置不用展示体或衬线体。大号独立数字(hero figure、stat-tile 数值)用默认比例数字;font-variant-numeric: tabular-nums 只保留给必须纵向对齐的列(表格行、轴刻度)。品牌适配时在此处替换成自己的 UI sans。

替换为品牌色板时的操作要点

把这份参考实例换成品牌色板,全部动作收敛为四步:

  1. 只改 palette.md 这一文件里的取值(分类 8 槽、顺序色阶、发散对、状态色、纹理、表面、墨水),方法与结构不动;
  2. 槽位顺序不要猜:枚举候选顺序,对每条跑校验器,只从通过者中选(color-formula.md 的 snap-to-passing 流程:每个槽取落在该模式亮度带内且 C ≥ 0.10 的步进,相邻对 ΔE < 8 时保持色相、只动亮度再跑);
  3. 对着自己的表面重跑:--surface <your-light> --mode light--surface <your-dark> --mode dark,散点/气泡/地图/small multiples 加 --pairs all,序级色阶加 --ordinal
  4. 任一 WARN 都须落实对应缓解(CVD 6–8 带 → 二级编码;低于 3:1 → 可见标签或表格视图),WARN 不可忽略,也不可用"换个图就没事了"绕过。

这套参考色板的价值不在于某个具体 hex,而在于它示范了"一份色板如何变成一组可计算、可复验、可替换的参数":同样的检查,任何设计系统都跑得动。

登录后查看全文
热门项目推荐
相关项目推荐