首页
/ Claude Code 数据可视化反模式目录:26 个图表错误与修正方法(system_prompts_leaks)

Claude Code 数据可视化反模式目录:26 个图表错误与修正方法(system_prompts_leaks)

2026-09-07 16:14:36作者:翟萌耘Ralph

本文基于 Claude Code 内置 Data Visualization 技能中的反模式清单 anti-patterns.md,完整拆解其中记录的 26 个真实生产事故——从双轴图、按排名重着色到虚线网格、骨架屏闪烁——并给出每一条的修正方法。文章进一步结合该技能的校验脚本 validate_palette.js 与参考色板 palette.md 的源码实现,说明这些"禁止事项"是如何被计算性规则(Delta E 阈值、色盲模拟、退出码语义)硬性执行的,读完你可以把这份清单直接当作图表交付前的质量门禁使用。

这份目录在整个 dataviz 技能中的位置

SKILL.md 定义了一条固定的七步工作流:先选形态(form heuristic),再按"颜色承担的职责"分配色,然后运行校验器而不是靠肉眼判断色板,接着套用图形规格(mark specs)、默认加上 hover 层、做可访问性终检,最后"渲染出来并亲眼看"。第七步之后,SKILL.md 明确要求把成品对照 references/anti-patterns.md——"它是出错的目录。如果你的图表命中任意一条,它就是错的"(见 SKILL.md)。

这份目录的开头即立下基调:

"Check every chart against this list. If your output matches an entry, it is wrong — fix it before shipping. These are real failure modes, each caught in shipping dashboards."

也就是说,它不是风格偏好清单,而是在已上线仪表盘上被抓到的真实失败模式,共 26 条,分为四组:颜色与编码(8 条)、形态(4 条)、图形与界面元素(9 条)、交互与可访问性(5 条)。下面逐条展开,并在每条之后给出仓库内的源码级佐证。

颜色与编码:8 个反模式

1. 双轴图(dual-axis chart)

  • 错误:在同一张图里放两个 y 轴。
  • 为何误导:两个刻度的对齐方式是任意的,图表会"发明"一个数据中不存在的相关性。原文档记录了一个真实案例:一张 "Adoption" 图把 Users(0–30k)和 Sessions(0–800k)画在一起,评审者直接指出它看起来像"幻觉出来的"。
  • 修正:拆成两张图,用小倍数(small multiples),或把两条序列归一化到同一基准(t0 时 = 100)再画在一条轴上。

源码印证:这是 SKILL.md "Non-negotiables"(所有设计系统下都不可妥协)里被点名为"第 1 号图表错误"的规则——"One axis. Never a dual-axis chart (two y-scales)"(SKILL.md);且 marks-and-anatomy.md 把优先级定为"直接标注优先于网格线,网格线优先于第二轴"。

2. 按筛选重着色(recolor-on-filter)

  • 错误:按"当前排名"分配颜色,导致筛掉某个序列后,幸存序列被重新涂色。
  • 为何误导:读者已经学到"Acme 是蓝色",重涂色直接破坏认知锚点。
  • 修正:颜色跟随实体(entity),而不是它的行号;幸存序列保持原色相。

源码印证:SKILL.md 同样将其列为不可妥协项:"Color follows the entity, never its rank. A filter that changes the series count must not repaint the survivors."(SKILL.md

3. 超过 8 个色相后循环/生成新色

  • 错误:出现第 9 个分类色——被"生成"出来或从已有槽位"复用"。
  • 为何误导:在色觉障碍(CVD)视角下,它和现有某个槽位无法区分;同时会破坏顺序检查。
  • 修正:把尾部并入 "Other",拆成小倍数,或使用复合编码(色相 × 形状)。

源码印证:参考色板固定为 8 个槽位(见 palette.md),choosing-a-form.md 明确"永远不要用生成更多色相来解决序列太多";全 8 色在 --pairs all 模式下本就无法通过全对校验(任何排序都不行),因此散点/气泡/小倍数这类形态被硬性限制在前三槽位。

4. 用肉眼判断"色盲安全"

  • 错误:"这几个颜色看起来区分度够了"。
  • 修正:运行 scripts/validate_palette.js。相邻色对在 CVD 模拟下的 Delta E 必须 >= 8(OKLab ×100),或落在 6–8 区间附带次级编码。

源码印证:这正是校验脚本的默认阈值,见 validate_palette.js

const CVD_TARGET = 8.0, CVD_FLOOR = 6.0; // OKLab Delta E×100, min(protan, deutan), adjacent pairs
const NORMAL_FLOOR = 15.0; // OKLab Delta E×100, worst pair on the active pairlist, unsimulated vision

注意脚本比清单多了一道正常视力地板:未模拟视线下最差的色对 Delta E 必须 >= 15,这是一道硬门禁——次级编码不能豁免它。Delta E 定义为 OKLab 空间中的欧氏距离 ×100(validate_palette.js),CVD 模拟使用 Machado-Oliveira-Fernandes (2009) severity 1.0 的色盲转换矩阵(validate_palette.js)——源码注释特别强调该模拟模型"是标准的一部分,不是实现细节",换用其他模拟(如 Viénot-1999)会使边界色对漂移、需要重新校准阈值。Python 孪生实现 validate_palette.py 使用完全一致的阈值常量,保证两套引擎结果锁定(lockstep)。

5. 在名义(无序)类别上用值渐变色

  • 错误:当类别没有自然顺序(产品、团队、端点)时,按数值大小把每根柱子涂成深浅不同的色。
  • 为何误导:它把柱长已经表达的数值再用色相编码一遍(双重编码),烧掉了唯一的自由通道来表达图表已经展示的信息;并且它按设计就会挂掉分类检查——一条 ramp 横跨明度带、最浅端会掉到彩度地板之下。
  • 修正:单一序列 → 所有柱子统一用槽位 1 的颜色;有序类别(漏斗、等级、年龄带)→ 用序数 ramp,并以 --ordinal 校验。

源码印证color-formula.md 给出判别标准——"交换类别顺序是否会改变含义";而 --ordinal 模式在源码里实现了四道序数专属检查:明度单调、相邻步长 ΔL >= 0.06、最浅端与画布对比 >= 2.0:1、色相散布 <= 40°(单色相)(validate_palette.js)。反过来,用分类校验器去校验一条正确的序数 ramp 会按设计 FAIL(因为它跨明度带、浅端彩度不足),这是预期行为,不应"修好"一条好 ramp 去迎合它(color-formula.md)。

6. 彩虹 / 非邻接的序列色

  • 错误:用多色相 ramp 表达数值大小。
  • 修正:单色相,浅 → 深。唯一的多色相例外是"类比邻色"或"语义热力",且必须配 scale legend。

源码印证:参考色板的序列色相是蓝色 100→700 共 14 步的完整 ramp(palette.md),并区分两种用途:连续量级编码允许最浅步退向画布;序数 ramp 的最浅端必须仍保持 2:1 对比(浅色模式不低于 step 250 #86b6ef,深色模式不深于 step 600 #184f95)。

7. 发散中点用色相 / 两极用两个冷色相

  • 错误:发散色标中点放一个彩色色相,或两极取两个冷色相。
  • 为何误导:中点必须读作"无",两极必须读作"相反"。blue ↔ aqua 失败(两者皆冷);blue ↔ red 或 blue ↔ orange 成功(冷暖对立)。
  • 修正:两个读作"相反"的色相 + 一个中性灰中点。

源码印证:参考色板明确记录了这个决策过程——"blue <-> red — warm/cool poles that read as opposite. Neutral midpoint is gray (light #f0efec, dark #383835). Equal step count per arm. (blue<->aqua was rejected — both cool, the midpoint doesn't read as 'nothing'.)"(palette.md)。

8. 状态色与非状态序列混用

  • 错误:把状态色(good/warning/serious/critical)用于非状态序列,或反过来把序列色用于状态。
  • 修正:只有当颜色确实意味着 好/坏时才用状态 token;表示身份时永远是分类色。

源码印证:状态色板是"固定、永不换肤"的四步(palette.md),且其色阶被刻意设计成与分类槽位"可区分到一眼不撞、但又不靠色相单独区分"的量级——例如浅色模式下 warning(#fab219,1.79:1)与 serious(#ec835a,2.57:1)按设计低于 3:1 对比,缓解手段是图标 + 标签配对,状态色从不单独承载语义。SKILL.md 进一步规定"一张图里状态与分类永不双用"(color-formula.md)。

形态:4 个反模式

9. 故事只是一个数字,却用了 8 个分类色相

原文称这是"图表最常偏离重点的方式"。

  • 修正:用强调法(highlight one, gray the rest),或直接用 stat tile / hero number。

10. 单柱条形图,或两片饼图

  • 修正:用 stat tile。数字本身就是图表。

11. 用环形/饼图比较接近的数值

  • 修正:用条形图,或干脆给数字。环形/饼图只用于"一眼看整体构成",且 <= 6 个分段。

12. 承载意义的颜色类别超过约 7 个

  • 修正:用表格,或表格 + 图表。超过约 7 个 bin 后,相邻类别会糊在一起。

源码印证:这四条的完整决策表在 choosing-a-form.md:单一当前值 → stat tile(而非单柱);少数头条数字 → KPI row;占比对限值 → meter(而非两片饼);>~7 类 → 表格(而非更多颜色)。其后的"序列数阶梯"(choosing-a-form.md)给出量化处置:1–3 条可直接标注;4 条必须强制直接标注(黄橙同屏)且全对形态封顶三条;5–6 为软上限;7–8 是 token 天花板,超过就并入 "Other"、facet 或复合编码。

图形与界面元素:9 个反模式

13. 粗饱和色块、重网格线、无呼吸感

放大后显得"聒噪"甚至幼稚。

  • 修正:细图形(thin marks)、退居幕后的发丝级网格/轴、慷慨的留白。饱和填充只用于小图形和强调,永不用于大块。

源码印证marks-and-anatomy.md 给出固定规格——柱 <= 24px 厚、数据线 2px 圆角接合、标记 >= 8px、面积填充为序列色相约 10% 不透明度("a wash, never a saturated block")、网格/轴为偏离画布一级的灰色发丝线。

14. 虚线网格线或轴线

虚线增加视觉噪声,且会被误读为"投影"或"阈值"——哪怕它只是网格。

  • 修正:网格线与轴线是实线发丝线,比表面深一级。

15. 每个数据点都标数字

点或分段旁全贴数值,是混乱且读不下去的。

  • 修正:>= 2 个序列时图例必须存在;直接标注有选择地做(端点、极值、唯一重要的那条序列),其余交给轴 + tooltip。

源码印证:"Label selectively — never a number on every point... Direct labels work because they are sparing — flood the chart and they stop working."(marks-and-anatomy.md

16. 给图形描边框来分隔

  • 修正:相邻填充之间用 2px 表面色间隙(堆叠分段与相邻柱同宽),重叠标记用 2px 表面色圆环(surface ring)。

源码印证:"Never draw a border around a mark to separate it. The gap and the ring are the mechanism; a stroke adds data-weight ink that isn't data."(marks-and-anatomy.md)——间隙与圆环就是分隔机制,描边加入的是"非数据的墨重"。

17. 标签被过小的柱/堆叠分段裁切或溢出

包括用 overflow: hidden 把分段内标签的首尾字符裁掉。

  • 修正:只有当文本在两侧留有余量地放得下时才渲染在图形内部;否则移到柱端外,或降级到 tooltip/图例(数值仍保留在表格视图中)。

源码印证marks-and-anatomy.md 明确"绝不用 overflow: hidden 去'解决'它——那会裁掉首尾字符,比没有标签更糟",并给出完整降级链:柱 → 移到柱端外 → 没地方就进 tooltip;内部堆叠分段(没有自由端)→ 跳过内联标签,交给图例 + tooltip;无论哪种,值都在表格视图里,不被"门控"。

18. 固定高度的图表容器把 x 轴带排除了

绘图区放得下,轴标签放不下,导致卡片出现一个小的嵌套纵向滚动条。

  • 修正:容器高度 = 绘图高度 + x 轴带,或干脆让容器随内容生长、不固定高度。

源码印证components.md 在 Tier 0 基础组件中把这条固化为容器规格:"Any fixed height includes the x-axis band (plot height + axis labels) so the card never gets a nested vertical scroll; prefer letting the container grow with its content."

19. 主角数字用展示体/衬线字体

读起来像"跑题的装饰"。

  • 修正:hero figure 与其余所有元素使用同一套无衬线体。

20. 大号独立数字上使用 tabular-nums

等宽数字会让 121 这种数字在展示尺寸下显得松散。

  • 修正:hero 与 stat-tile 数值用比例数字;tabular-nums 只用于需要纵向对齐数字的地方(表格行、轴刻度)。

源码印证palette.md 的"Typeface & figures"一节规定:一切——包括 hero figure——都用系统无衬线 system-ui, -apple-system, "Segoe UI", sans-serif,"No display or serif face anywhere",并复述了比例/等宽数字的分工规则。marks-and-anatomy.md 则补充 hero figure 规格:>= 48px、每个视图恰好一个、无衬线。

21. 纹理默认开启,或作为装饰

密集斜纹场是有前庭风险(vestibular risk)的,在数值色标上读作噪声。

  • 修正:纹理是可选开启(a11y 设置、打印、forced-colors 触发),只允许 45°/135°,在数值色标上必须有序(rotation 随量级步进、臂角承载发散符号)。

源码印证palette.md 只定义了一个手绘 "Lines" 纹理填充,"Inked tone-on-tone... never decorative, never on by default";marks-and-anatomy.md 补充"永不使用水平/垂直纹理——它们会被读作网格线/条形"。

交互与可访问性:5 个反模式

22. tooltip 是读取数值的唯一途径

  • 修正:tooltip 是增强,永不设闸——每个值都能通过直接标注或表格视图到达;键盘焦点显示与 hover 相同的内容。

源码印证interaction.md 将 "Tooltips enhance, they never gate" 列为交互总则,并要求键盘焦点与 hover 详情一致。

23. 针尖级 hover 目标

一个 8px 散点必须正中命中的体验。

  • 修正:命中区域包含 2px 间隙且满足约 24px 最小尺寸;密集散点用最近点/Voronoi 层。

源码印证interaction.md:"give each point a transparent hit area of at least 24px, or — for dense scatter — a nearest-point / Voronoi layer so the pointer only has to be closest, not dead-center." 且该命中区域与 marks-and-anatomy 中的 2px 表面圆环是同一套机制。

24. 每图独立筛选器,或把筛选器放进图表卡片

  • 修正:一行筛选器放在它管辖的所有内容之上;所有图表对同一切片重新渲染。

源码印证interaction.md:"One row, above the charts... never inside a chart card, never per-chart. If one chart needs its own range, it's a different dashboard."

25. 重新拉取时骨架屏闪烁

  • 修正:保留上一帧渲染、降低不透明度——零布局跳动。

源码印证interaction.md:"charts hold their previous render at reduced opacity — no skeleton, no layout jump, no flash."

26. 没有表格视图 / 连续色标仅靠颜色编码

  • 修正:每张图都有一个"表格视图孪生"(the WCAG-clean equivalent)。

源码印证components.md 把"table-view toggle"定义为图表容器的固有组件,并在 System tier 中列出 "Table-view generator — the WCAG-clean equivalent of any chart"(components.md)。

源码级印证:反模式规则如何被"执行"

上面的清单之所以能称为门禁,是因为它背后有一套可运行的计算逻辑。以下从仓库源码中确认的关键事实,可以作为每条规则的落地依据:

1. 阈值即规则。 validate_palette.js 将文档中的每个"数字"固化为常量:明度带浅色 0.43–0.77 / 深色 0.48–0.67(OKLCH L)、彩度地板 0.10、CVD 目标 8 / 地板 6、正常视力地板 15、画布对比 3:1、默认画布浅色 #fcfcfb / 深色 #1a1a19。这与 color-formula.md 的"六道检查"一一对应——其中第 1 道(固定色相顺序)与第 6 道(只允许文档化色板值)是结构性规则,由技能流程强制,无法从 hex 本身测量(validate_palette.js)。

2. 成对校验策略区分图表形态。 源码中 pairlist 的构造(validate_palette.js)解释了为什么清单反复强调"散点/气泡/小倍数只能到三条序列":堆叠/柱/折线只有相邻色对会相邻,默认 --pairs adjacent 即可;而全对形态下任意两个标记都可能并置,--pairs all 是全 8 色在任何排序下都无法通过的场景——这正是"序列数阶梯"中硬封顶 3 的数学原因(choosing-a-form.md)。

3. 退出码语义可直接接入 CI。 Node CLI(validate_palette.js)支持 --mode light|dark--surface #hex--pairs adjacent|all--ordinal,参数错误退出 2,任一硬 FAIL 退出 1;CVD 6–8 地板带与 <3:1 对比只报 WARN 且退出 0——但这两类 WARN 都"合法地"要求次级编码(直接标注/间隙/纹理)。Python 孪生 validate_palette.py 阈值与之一致。

4. 浏览器内自校验。 同一脚本可作为 <script type="module"> 载入图表页面,读取 <body>data-palette / data-mode / data-pairs / data-surface 属性,输出 console.table 报告(validate_palette.js)。这与 palette.md 建议的"把槽位定义为 CSS 自定义属性、按角色引用"的做法形成闭环:页面声明的色板与页面实际渲染的色板是同一份数据,校验失败会 console.warn 直接出现在图表自身的控制台里。

5. 输入边界处理。 两个引擎都把用户输入的 hex(色板与画布 alike)统一经过同一套空白剥离与格式校验(validate_palette.js),注释解释为何手写正则而非依赖引擎原生 trim——JS trim() 与 Python str.strip() 在 Unicode 边界行为不同,共享集合取两者交集,以覆盖从渲染页面复制 hex 列表时带入的 NBSP/全角空格。这解释了为何"肉眼检查"之外还必须跑校验器:连输入规范化都是双引擎锁定的。

6. 参考色板本身就是反模式的解法汇总。 palette.md 记录了全部参数的"参考实例":8 槽分类色(含 2026 年 7 月同色相重排的完整决策史——为更好的开局色牺牲第 4 槽的全对校验)、14 步序列 ramp、blue↔red 发散对(附 blue↔aqua 被拒原因)、固定状态色板及双模式对比度实测值、纹理规格、以及图表 chrome/墨色全套 token。文中还提示:替换为品牌自己的色阶后必须用自己的画布重跑(--surface <your-light> --mode light 等),因为"对比与明度带的结果只有对图表真正渲染的画布才有意义"(palette.md)。

实战自查表

交付任何图表前,可按下表逐行打勾;左列是反模式(出自 anti-patterns.md),右列是仓库内可验证的依据:

# 自查问题 对应修正 仓库依据
1 是否只有一条 y 轴? 双图 / 小倍数 / 归一化到 t0=100 SKILL.md
2 筛选后序列是否保持原色? 颜色跟随实体 SKILL.md
3 分类色是否 <= 8 槽、无第 9 色? Other / facet / 复合编码 choosing-a-form.md
4 色板是否跑过校验器(含 dark 模式)? node scripts/validate_palette.js validate_palette.js
5 名义类别是否误用了值 ramp? 单序列统一 slot 1 / 有序用 --ordinal validate_palette.js
6 序列色是否单色相浅→深? 14 步单色 ramp palette.md
7 发散两极是否冷暖对立、中点中性灰? blue↔red + gray palette.md
8 状态色是否只用于状态? 状态 token + 图标 + 标签 palette.md
9 单数字是否用了 stat tile 而非 8 色图? stat tile / hero number choosing-a-form.md
10 是否有单柱/两片的"伪图表"? stat tile / meter 同上
11 饼/环是否只用于 <=6 段整体构成? 改为条形或数字 同上
12 有意义的色类是否 <= 7? 表格或表格 + 图表 同上
13 大块是否用了饱和填充? 细图形 + 发丝网格 + 留白 marks-and-anatomy.md
14 网格/轴是否实线发丝? 一阶灰实线 同上
15 是否只在关键点直接标注? 端点/极值/主角序列 marks-and-anatomy.md
16 图形分隔是否靠描边? 2px 表面间隙 + 2px 表面圆环 marks-and-anatomy.md
17 标签是否被裁切/溢出(含 overflow: hidden)? 先测量再放置;否则外移/tooltip marks-and-anatomy.md
18 固定高度是否包含 x 轴带? 高度 = plot + 轴带,或不固定 components.md
19 hero 数字是否无衬线? 全站同一 sans palette.md
20 大号独立数字是否误用 tabular-nums? 比例数字;对齐列才等宽 marks-and-anatomy.md
21 纹理是否默认开启? 仅 a11y/打印/forced-colors,45°/135° marks-and-anatomy.md
22 数值是否只能靠 tooltip 读? 直接标注/表格视图兜底;焦点 = hover interaction.md
23 命中目标是否 >= 24px? 透明命中区 / Voronoi 层 interaction.md
24 筛选器是否一行、在图表之上? 单行 filter row,全图同切片 interaction.md
25 重拉数据是否骨架闪烁? 保留上一帧、降不透明度 interaction.md
26 是否每张图都有表格视图孪生? table-view toggle 为容器固有组件 components.md

这份目录的价值在于它把"审美判断"压缩成了"命中即错"的判别规则,并让其中可计算的部分(色板安全性)真正变成了一条命令。对于使用 Claude Code 或自建图表系统的团队,最务实的落地路径是:保留这份 26 条清单作为 code review 检查项,把 validate_palette.js(或其 Python 孪生)接入色板变更的自动化校验——WARN 不阻断但要求次级编码,FAIL 阻断交付。

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

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
934
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.96 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23