Diagram Design:把编辑级图表品味写成 45 种图型与 32 个机器校验器的 Agent Skill
cathrynlavery/diagram-design
Cathryn Lavery 的 diagram-design(MIT,54,318 stars)让 agent 用一句自然语言产出单文件 HTML+SVG 的编辑级图表:45 种图型各有独立 reference,语义模式先于版式;六条连线硬规则、4px 网格与每图 ≤9 节点的复杂度预算把「品味」变成可判定条款;.drawio / Mermaid / Excalidraw 只抽取不渲染,按 format×size×detail×audience 四个旋钮重画并交保真清单;每张图的 HTML 带 data-* 元数据契约,32 个 verify-* 校验器逐条审计数字与几何。
编辑判断最值得抄的是它把「品味」拆成三类可判定的机器契约:颜色是语义角色加对比度契约(verify-contrast.py 对每个角色在它真实所处的背景上算 WCAG,半透明色先合成再量),几何是 4px 网格加每图 ≤9 节点、≤12 箭头、≤2 个 accent 的预算,数字是 HTML 里的 data-value / data-carry / data-status 声明加 32 个 verify-* 校验器重算。我本地实测:把 shipped waterfall 示例的一个 delta 从 −38 改成 −36,verify-waterfall.py 立刻报「'FY26 plan' (bar 6): declares 236 but the walk arrives at 238」;mermaid_extract.py 对 9 节点 fixture 吐出节点/边普查、hub 度数、入口与终点、可折叠组与预算旗标;对抗性 Excalidraw fixture 里的「IGNORE ALL PREVIOUS INSTRUCTIONS」只留下被转义的惰性标签,2 个链接与 1 个 embed 进 discarded 列表。边界它自己说得同样诚实:README 明说输出质量取决于模型、同一 prompt 两次排版可能不同、产物不是可 diff 的源格式,「Git 里每 sprint 都变的文档请用 Mermaid」;PNG 导出要一次性装 Playwright 加 Chromium;9 节点预算意味着复杂系统必须先拆 overview 加 detail;210 个预置示例还是旧皮肤下生成的,重生成是挂着的 v5.1 任务。
npx skills add cathrynlavery/diagram-design模型懂内容,缺的是品味:一个把「编辑级图表」写成机器契约的 skill
diagram-design(MIT,54,318 stars / 3,442 forks,作者 Cathryn Lavery,官网 diagramdesign.dev,插件版本 2.6.98)是一个跨运行时的 Agent Skill:Claude Code、Codex、GitHub Copilot、Factory Droid、Pi、Cursor、Gemini CLI、Kiro、OpenCode 都能装。它让 agent 用一句自然语言需求产出一个自包含 HTML 文件——内联 SVG、内嵌 CSS、默认零 JavaScript——双击就能在浏览器打开,也可以导出 SVG / PNG。仓库的自我定位很直接:45 种图型,没有阴影,没有 Mermaid slop。
作者的出发点写在 README 的「Why I built it」:给博客配图时,问 Claude 要一张架构图,拿回来的是「和站点其它部分毫不相干的圆角方块」;要么跟 Figma 搏斗三十分钟,要么干脆不画图。「模型已经理解了内容,缺的是品味,于是我把品味写了下来」——一个 accent 色只给一到两个焦点、1px 发丝线、没有阴影、每个坐标落在 4px 网格上。这句话是整个仓库的纲:后面 63 个 reference、210 个示例、87 个脚本,全部是这句品味的可执行展开。

type-architecture 的正典示例:焦点只有一个(accent 描边 + accent-tint 填充的 Astro Origin),连线全部正交圆角,箭头标签坐在不透明遮罩上并与线段留白,图例是底部横条而不是浮在图里的盒子——§4 反模式表里点名的毛病一个都没有。45 种图型 + 9 个语义模式:先判行为,再选版式
skill 的分类法是两层:语义模式描述系统在做什么,视觉图型描述信息怎么摆。当行为、状态、执行点或风险才是图的意义时,先加载 semantic-patterns.md 选一个主模式,再选最近的视觉图型当版式语法;没有模式命中就直接选图型。45 种图型各自有一个 references/type-*.md(CI 的 verify-docs-sync.py 把 VISUAL_TYPE_COUNT = 45 钉死:选型表、frontmatter description、gallery 编号、README 计数四处必须一致),覆盖 architecture、architecture delta、IT current-state、flowchart、sequence、state、ER、timeline、swimlane、quadrant、radar、polar、loop/flywheel、nested、tree、org chart、layer stack、exploded axonometric、axonometric plan、venn、pyramid/funnel、treemap/marimekko、heatmap、bar/dumbbell、waterfall、line(slopegraph/ridgeline/streamgraph/bump)、gantt、scatter(bubble/beeswarm)、high-level、process、medallion、data flow、DP integration、DP security matrix、sankey、fishbone、wardley、kanban、journey、service blueprint、deployment、dependency、UML class、story map、db schema。
| 读者必须理解…… | 语义模式 | 最近图型 |
|---|---|---|
| 多个到达流竞争有限服务能力 | Fan-in queue / bottleneck | Data flow |
| 跨阶段重复的问题 / 输入 / 控制 / 输出槽位 | Stage framework with semantic slots | Process |
| 松散对话变成持久的结构化记录 | Unstructured input → structured artifact | Data flow |
| 两条策略判定为何不同、在哪里首次分叉 | Paired policy-evaluation traces | Flowchart |
| 哪些路径穿越信任边界、哪些被拦 | Secure paved road | Architecture |
| 每个执行面上适用哪些控制 | Governance / control catalog | Layer stack |
| 防御如何降低风险、还剩多少风险 | Compensating security layers | Layer stack |
| 系统分解成哪些可独立引用、可追溯到实现的子块 | Traceable block decomposition | Tree |
| 一个主体如何经历阶段、等待、重试、取消与终态 | Lifecycle phase map | State machine |
选型纪律同样写在明处:「如果一张三列表格能传达同样的信息,就选表格」;两个图型都想用时选主导轴,语义模式最多再借一个 primitive,不能引入第二套版式语法;超过复杂度预算就拆成 overview + detail。画之前还要先说一句计划(图型、模式、尺寸预设、打算砍掉什么),用户在场就先让用户改方向。
设计系统:语义角色、一个焦点、4px 网格与复杂度预算
style-guide.md 是颜色与字体的唯一事实来源,所有 reference 只写语义角色名(accent 而不是 hex)。默认皮肤是一套冷调编辑色:white-smoke 纸 #f5f5f5、jet-black 墨 #2d3142、blue-slate 次级 #4f5d75、burnt-tangerine 焦点 #bf4520。注意 accent 不是品牌原色 atomic-tangerine——原色在纸上只有 2.86:1,当文字太淡,所以皮肤用的是同色相压暗到 4.5:1 以上的值(实测 4.71:1)。对比度是契约不是建议:verify-contrast.py 对每个角色在它真实所处的背景上算 WCAG 比值,半透明色先合成再量,文本 4.5:1、必要图形 3:1,light/dark 两套皮肤都跑,CI 不过就红。
| 角色 | 用途 | light | dark |
|---|---|---|---|
paper / paper-2 | 页面底 / 容器底 | #f5f5f5 / #ececec | #2d3142 / #393e53 |
ink | 主文字、主描边 | #2d3142 | #f5f5f5 |
muted / soft | 次级文字、默认箭头 / 子标签 | #4f5d75 / #5a6580 | #bfc0c0 / #949eb2 |
accent / accent-tint | 焦点(每图 ≤2)/ 焦点填充 | #bf4520 / 8% 透明 | #f08a59 / 10% 透明 |
link | HTTP/API 调用、外部箭头 | #2e5aa8 | #739fdf |
字体三族各司其职:Instrument Serif 只给 H1 与斜体批注,Geist sans 600 给节点名,Geist Mono 只给技术性子标签(端口、命令、URL)——「Mono 是技术内容的字体,不是一种 dev 审美」,并明令禁止 JetBrains Mono。结构几何全部落在 4px 网格:节点宽高只允许 80/96/112/120/128/140/144/160/180/200/240/320,间距 20/24/32/40/48,内边距 8/12/16,圆角 4/6/8;字号不走网格,走 output-spec.md 按尺寸预设的角色字阶。
复杂度预算是这套品味的硬边界:每图 ≤9 节点、≤12 箭头、≤2 个 accent 元素、≤2 个批注 callout,超了就拆两张。每种图型还有自己的行(layout-budget.md):sequence ≤5 条 lifeline、≤1 个 combined fragment(两个仅当都是单区域 opt/loop)、≤2 个 alt 区;ER ≤8 实体;sankey ≤3 阶段 / 8 节点 / 12 流;waterfall ≤8 根柱(含总计、1 个小计);db schema ≤5 表 / 显示 8 列 / 6 外键;scatter ≤30 点;venn ≤3 圆;radar ≤5 轴 5 系列且焦点系列 1 个。语义模式自己的预算只能更紧,不能更松。

data-* 声明,verify-sankey.py 会重算守恒与丝带几何。accent 在这里只承担一件事——flaky 重跑那两条丝带。六条连线硬规则与「taste gate」
§6 的六条连线规则被写成「违反即自动失败」:① 只允许正交连线,离轴节点之间是 r=8 圆角肘折,斜线直接判死;② 每个箭头标签(≤14 字符、全大写、居中)坐在不透明遮罩上,与自己的线段留 6–10px 可见间隙,竖线段旁而不是线上;③ 不允许重叠或叠放的连线,平行路由错开 ≥12px,交叉处用 bridge/hop;④ 同一条边上的多个连接点按 L·k/(N+1) 均分、相距 ≥12px;⑤ 不允许从非端点盒子背后穿过,几何上避不开时才用虚线 + 端点标签 + 不给中间盒子箭头;⑥ 标签遮罩不能被后画的节点盖住。绘制顺序也定死:背景 → 区域 → 箭头 → 标签 → 节点,先画箭头让线压在节点下面。
§4 的反模式表是给「AI 味」列的通缉令:暗色 + 青紫辉光、把 JetBrains Mono 当万能 dev 字体、所有节点同一种盒子、浮在图内的图例、没有遮罩的箭头标签、竖排文字、三张等宽总结卡、任何阴影、rounded-2xl、每个「重要」节点都上 coral、以及「复刻 Mermaid 渲染器的自动布局」。§9 的 pre-output checklist 在出图前逐条过:图型是否合适、表格/段落是否就够、是否加载了对应 type reference、六条连线规则、4px 网格、可访问 SVG 契约、self_check.py 是否打印 OK。
导入是「抽取—重画」,不是转换:四个旋钮与保真清单
对已有的 .drawio / Mermaid / Excalidraw,skill 的立场是 extract, don't render;redraw, never convert。三个纯标准库的抽取器(drawio_extract.py 33.8KB、mermaid_extract.py 56.1KB、excalidraw_extract.py 26.4KB)把源解析成同一种 digest:节点/边普查、容器与深度、形状分布、hub 度数(焦点候选)、入口与终点、未连接节点、可折叠组、预算旗标。源里的标签、链接、指令、元数据一律当不可信数据处理。源坐标、源配色、源字体、draw.io 的斜线 spaghetti、Mermaid 的自动布局全部丢弃;保留的是组件、关系、分组与方向。
| 旋钮 | 取值 | 改什么 |
|---|---|---|
| Format | html · svg · png · html+png | 交付物:SVG 给 Figma,PNG 给幻灯片,HTML 给网页 |
| Size | doc-inline 960×600 · doc-wide / slide-16x9 1280×720 · slide-4x3 · social-og 1200×632 · social-square · print-a4/a3/letter · fit | viewBox 与字阶:投影片用 16px 节点名而不是 12px |
| Detail | faithful(≤24 节点、分区)· balanced(≤12,默认)· simplified(≤7) | 源里有多少活下来,按固定降级梯:装饰 → 重复 → 叶簇 → 基础设施 |
| Audience | engineer · mixed · executive | 只改措辞不改数量:Auth Service / JWT · RS256 · :8443 → token check → Sign-in |
每次导入结束要交一份保真清单:合并了什么、折叠了什么、丢了什么。README 给的样例很诚实——「Collapsed: “Token valid?” decision → edge label on Gateway → Auth;Dropped: 1 sticky note (“legacy path, to be retired”), unconnected in source」。作者的理由是:用户知道源长什么样,你偷偷丢了他也会发现。

balanced 档被重画的结果:源里六个粉彩填充变成一个 accent,手拖坐标变成 4px 网格。抽取器先报 budget: nodes OVER (max 9),于是降级决策在画之前就发生了。输出既是单文件 HTML,也是机器可读的契约
这是整个仓库最硬的一层设计:每种图型的输出 HTML 带一套 data-* 元数据契约(data-diagram、data-snapshot、data-grid、data-object-id、data-status、data-signature、waterfall 的 data-value/data-carry、block decomposition 的 data-block-*),图在声明自己画了什么;仓库里 32 个 verify-*.py 则逐条重算这些声明——waterfall 的 running total 守恒、sankey 的流量守恒、treemap/marimekko 的面积与标注值一致、slopegraph 两端共用一把尺且端点等于打印值、beeswarm 的点精确落在值上且不叠印、ridgeline 共享振幅、heatmap 单调不透明度梯 + 完整 N×M 网格 + ≤1 焦点格、bubble 的面积编码每个声明尺寸、architecture-delta 的 Before/After 网格与变更台账一致。每个校验器都配一个 test-verify-*.py 对抗用例(41 个),确保校验器自己也会被抓。
我在本地 checkout 上跑了几条,全部零依赖、Python 3.13 直接可用:
| 命令 | 实测结果 |
|---|---|
self_check.py assets/example-architecture.html | OK(可访问 SVG 契约、单文件安全、内容在 viewBox 内) |
verify-geometry.py assets/example-architecture.html | 0 findings(标签遮罩未被后画节点裁切、无斜线/叠线/角点端口) |
verify-waterfall.py assets/example-waterfall.html | OK … conserve the running total and draw it where they claim |
verify-waterfall.py fixtures/waterfall-plausible-wrong.html | FAIL:bar 6 “FY26 plan” declares 236 but the walk arrives at 238——该 fixture 与正典示例只差一个 delta(−38 被改成 −36) |
mermaid_extract.py fixtures/sample-flowchart.mmd | 9n/7e digest:形状分布、hub(API Gateway 度 6)、入口/终点、未连接 note、两个可折叠组、预算 ok |
drawio_extract.py fixtures/sample-architecture.drawio | 12n/8e,budget: nodes OVER (max 9) → 画之前就必须做降级决策 |
excalidraw_extract.py fixtures/sample-adversarial.excalidraw | 注入文本被转义成普通标签;discarded: 1 image payloads, 2 links, 1 embeds, 1 deleted, 1 unknown——链接不跟随、payload 不执行 |
export_svg.py assets/example-flowchart.html out.svg | 8.7KB 独立 SVG:role="img" + aria-labelledby、CDATA 携带 class CSS、@import 字体、defs ID 加命名空间 |

data-carry 声明累计值,校验器重走一遍加法。把其中一个 delta 改 2,CI 就会点名到第几根柱、声明值与实走值各是多少。渲染层的门禁同样不靠肉眼:lint-render.py(1,679 行)把每个示例丢进 headless Chromium,先按原样截图、再分级释放 overflow 截第二张,两张做像素差——多出来的墨就是被裁掉的内容;lint-skin.py 读源码查皮肤合规(颜色、字体、单文件安全、脚本白名单);verify-docs-sync.py(1,765 行)盯 16 类文档漂移,包括「frontmatter description 必须保留每个图型的词钩子」与「SKILL.md 不得超过 40,000 字节」(ADR 0004:description 是 agent 决定要不要加载 skill 之前唯一看到的文字,路由词永远不许拿去做正文瘦身)。
渐进披露:SKILL.md 只是路由器
安装后的 agent 启动时只看到 skill 名与 description;请求命中才加载 SKILL.md(30,778 字节,cap 40,000),再按需拉一个 type reference;行为重的图加 semantic-patterns.md,要动效才加 animation.md,要精确 markup 才加 primitives-core.md,要某图型的预算行才加 layout-budget.md。README 的「What loads when」表把这件事写成契约:「Make me a flowchart」= SKILL.md + type-flowchart.md,仅此而已。「明天加一种图型,其它什么都不变。」210 个示例 HTML 与 47 张正典截图(render-canonical-screenshots.py 确定性渲染 + verify-screenshot-freshness.py 盯过期)让每种图型都有可直接打开的参照。
品牌化走 onboarding:一句「onboard diagram-design to https://yoursite.com」,agent 抓首页、抽主色与字体栈、映射到语义角色、给出 diff、写进 style-guide.md,并附一份保真收据(采样 URL、每个颜色角色、字体族与字重、字体来源、任何回落)。写 token 之前先验 WCAG AA,站点颜色在 9–12px 图字号下对比不足时会给出调整值并解释原因。多客户场景用命名 profile(~/.diagram-design/profiles/<slug>.md)+ 项目里的 .diagram-design marker(ADR 0006),并行工作区互不覆盖。首次运行还有一道 gate:风格指南仍是出厂默认时,skill 会停下来问「现在定制、贴 token、还是就用默认」,不允许把默认皮肤悄悄塞进一个有品牌的项目。

verify-axonometric-plan.py 逐条查,示例由 build-axonometric-plan-examples.py --check 保证与生成器同源。分发与 CI:六个宿主 manifest、83 条本地门禁
仓库同时维护 .claude-plugin/(marketplace + plugin)、.codex-plugin/、.factory-plugin/ 三套原生 manifest,commands/ 6 个斜杠命令(export-diagram / import-drawio / import-mermaid / import-excalidraw / profile / doctor)、prompts/ 5 个 Pi 模板,以及跨宿主的 npx skills add cathrynlavery/diagram-design。版本号由 Auto Version Bump 工作流在合并进 main 之后统一抬(ADR 0009),PR 里改 manifest 版本会被 CI 拒——开放 PR 之间不再因为版本号互相冲突。.maintainer-policy.json 把 83 条本地门禁命令、squash 合并、每人最多 5 个开放 PR 等维护契约写成机器可读文件,并有 test-maintainer-policy.py 盯它与 manifest、CI 的一致性。CI 矩阵是 3 操作系统 × Python 3.11/3.12,像素级比对只在 ubuntu+3.12 上做(避免六个 oracle 互相比),其余腿跑全部源码级门禁。
隐私面也写得干净:PRIVACY.md 声明没有服务器、没有账号、没有分析、没有遥测;唯一的网络请求是浏览器打开成品时加载 Google Fonts(可改 system fonts 做到零请求),以及用户主动要求 onboarding 时 agent 自己抓的那几页网站。/doctor 是一次性环境诊断:解析 python3(Windows 的 Microsoft Store 别名 stub 不算数)、要求 ≥3.10、检查 Playwright 与命令路由,只读不改用户文件。

实测判断与边界
它最值钱的地方不是 45 种版式,而是把「品味」拆成了三类可判定物:语义角色与对比度契约(颜色)、4px 网格与复杂度预算(几何)、data-* 声明 + 32 个校验器(数字)。绝大多数「设计类 skill」停在形容词,这个仓库停在 CI。导入链路的立场也少见地正确:不渲染源、不复用源坐标、把源文本当不可信数据、最后交保真清单——对抗 fixture 里那句「IGNORE ALL PREVIOUS INSTRUCTIONS」被转义成一个普通节点标签,链接与 embed 直接进 discarded 列表。
边界它自己说得同样清楚:README 的对比表承认输出质量取决于跑的模型、同一条 prompt 两次排版可能不同、产物不是可 diff 的源格式(PR 里没有文本可 review、没有画布可拖),「每 sprint 都在变的文档图,Mermaid 仍然是更合适的选择——Diagram Design 是给那张你要发布的图用的」。PNG 导出需要一次性装 Playwright + Chromium;≤9 节点的预算意味着真实大系统必须先拆 overview + detail;210 个预置示例是旧皮肤下生成的,README 注明按当前 style-guide 重生成是 v5.1 的待办。动效是可选表现层(none/reveal/step/loop,ADR 0003 只允许 reveal 自动播放),无 JS 与 prefers-reduced-motion 下必须呈现完整静态帧。
安装
# 跨宿主(Cursor / Claude Code / Codex / Copilot / Gemini CLI / Cline / Windsurf / Amp…)
npx skills add cathrynlavery/diagram-design
# Claude Code(marketplace,含斜杠命令与自动更新)
/plugin marketplace add cathrynlavery/diagram-design
/plugin install diagram-design@diagram-design
# Codex
codex plugin marketplace add cathrynlavery/diagram-design
codex plugin add diagram-design@diagram-design
# 出厂自检(装好后对任意成品跑)
python3 <skill-dir>/scripts/self_check.py my-diagram.html