HTML 图表生成器 是做什么的?
生成自包含的 .html 文件,其中包含一个内联 <svg>,用于进行视觉论证,而不仅仅是展示信息。然后使用 Playwright 截图,检查生成的 PNG,并不断修复直到图表干净整洁。
此技能包含的文件
| 文件 | 用途 | 何时读取 |
|---|---|---|
references/color-palette.md | 所有颜色和品牌样式:单一真实来源 | 每次创建图表前 |
references/visual-patterns.md | 每种布局模式的草图(扇出、时间线、树形...) | 设计步骤 2 |
references/starter.html | 页面壳:CSS 令牌、文本/形状类、箭头标记、每种约定的一个示例 | 复制它作为每个图表的起点 |
scripts/render.py | 将 HTML 截图成 PNG 并进行几何检查 | 每次编辑后运行(请参阅「渲染与验证」) |
examples/ | 两个完成的图表及其 PNG 图片 | 当您需要密度和样式参考时 |
要为一个品牌重新设计所有图表的样式,请编辑 color-palette.md(以及 starter.html 中对应的令牌)。其他所有内容都是通用方法。
两个已做出的决定(不要在每次生成图表时重新讨论)
1. 一切都是 SVG。HTML 只是其周围的页面。
框、文本、箭头和代码块都位于一个 <svg id="diagram"> 中。不要使用 HTML/CSS(flexbox/grid div)构建框,然后叠加 SVG 连接器。
原因: 图表是一个几何问题,箭头两端需要精确的坐标。使用全 SVG 方式,每个节点和每个箭头共享一个坐标系,该坐标系直接写在文件中,因此您可以通过算术计算箭头端点,并通过编辑数字来修复它。使用 HTML 框时,位置来自浏览器的布局引擎:它们不在源代码中,会随字体和视口宽度而变化,连接器随后需要 JavaScript 在运行时测量框。这是需要调试的另一个程序,并且读者的视口可能与您验证时的视口不同。全 SVG 的代价是真实存在但更小:SVG 文本不会自动换行(您需要使用 <tspan> 手动换行),并且其宽度直到渲染后才可知(lint 工具会测量并报告溢出)。
<foreignObject>(SVG 内部的 HTML)默认关闭:它是唯一能获得文本换行的地方,但在浏览器外渲染不一致,且 lint 工具无法检查它。仅当用户需要在形状内放置段落时才使用它。
2. 不使用 Graphviz 或 D2 进行布局。手动放置坐标。
原因: 该方法要求布局反映概念本身:扇出、时间线、循环、并排比较、自由浮动注释、放置在其说明步骤旁边的代码块。自动布局引擎针对不同的目标进行优化(分层图,最小化交叉边),因此它们将每个图表扁平化为相同的自上而下的层次结构,而这正是本技能旨在避免的“统一方块”输出。它们还引入了二进制依赖,并生成难以重新样式化和注释的 SVG。
例外情况: 对于真正的图形化主题(大约 15 个以上具有许多交叉链接的节点,如依赖关系图),交叉是主要问题。在这种情况下,仅使用 dot -Tplain(Graphviz)获取节点的坐标,然后使用本技能的样式和约定自行绘制节点。请说明您正在这样做以及原因。
核心哲学
图表应该论证,而不是展示。
图表不是格式化的文本。它是一种视觉论证,展示文字无法单独表达的关系、因果关系和流程。形状本身应该就是意义的载体。
同构测试:如果移除所有文本,仅凭结构能否传达概念?如果不能,重新设计。
教育测试:人们能否从该图表中学到具体的东西,还是它仅仅给几个框贴了标签?一个好的图表能起到教学作用——它展示实际的格式、真实的事件名称、具体的例子。
深度评估(首先执行此步骤)
在设计之前,确定该图表需要何种详细程度:
简单/概念性图表
在以下情况使用抽象形状:
- 解释思维模型或理念
- 观众不需要技术细节
- 概念本身就是抽象(例如“关注点分离”)
全面/技术性图表
在以下情况使用具体示例:
- 绘制真实系统、协议或架构的图表
- 图表将用于教学或解释(例如 YouTube 视频)
- 观众需要理解事物的实际外观
- 您在展示多个技术如何集成
对于技术图表,您必须包含证据工件(见下文)。
研究要求(针对技术图表)
在绘制任何技术性内容之前,研究实际的规范。
如果您正在为协议、API 或框架绘制图表:
- 查找实际的 JSON/数据格式
- 找到真实的事件名称、方法名称或 API 端点
- 理解各部分实际上是如何连接的
- 使用真实的术语,而不是通用的占位符
错误示例:“协议” → “前端” 正确示例:“AG-UI 流式传输事件 (RUN_STARTED, STATE_DELTA, A2UI_UPDATE)” → “CopilotKit 通过 createA2UIMessageRenderer() 渲染”
研究使图表既准确又有教育意义。
证据工件
证据工件是证明图表准确性并帮助观众学习的具体示例。在技术图表中包含它们。
证据工件的类型(选择与您的图表相关的):
| 工件类型 | 何时使用 | 如何渲染 |
|---|---|---|
| 代码片段 | API、集成、实现细节 | 深色矩形 + 语法着色文本(请参阅颜色调色板中证据工件的颜色) |
| 数据/JSON 示例 | 数据格式、模式、负载 | 深色矩形 + 彩色文本(请参阅颜色调色板) |
| 事件/步骤序列 | 协议、工作流、生命周期 | 时间线模式(线 + 点 + 标签) |
| UI 模型 | 展示实际输出/结果 | 模仿真实 UI 的嵌套矩形 |
| 真实输入内容 | 展示输入到系统的内容 | 包含可见示例内容的矩形 |
| API/方法名称 | 真实的函数调用、端点 | 使用文档中的实际名称,而不是占位符 |
示例:对于流式传输协议的图表,您可能会展示:
- 规范中的实际事件名称(而不仅仅是“事件 1”、“事件 2”)
- 展示如何连接的代码片段
- 流式传输的数据实际看起来是什么样的
示例:对于数据转换管道的图表:
- 展示样本输入数据(实际格式,而不是“输入”)
- 展示样本输出数据(实际格式,而不是“输出”)
- 如果相关,展示中间状态
关键原则:展示事物实际的样子,而不仅仅是它们的名称。
多缩放架构
综合性图表同时以多个缩放级别运作。可以把它想象成一张既显示国家边界又显示街道名称的地图。
级别 1:概要流程
一个简化的概览,一目了然地展示完整的流程或管线。通常放置在图表的顶部或底部。
示例:输入 → 处理 → 输出 或 客户端 → 服务器 → 数据库
级别 2:部分边界
标记区域,用于分组相关的组件。这些创建了视觉上的“房间”,帮助观众理解哪些组件属于一起。
示例:按职责分组(后端 / 前端),按阶段分组(设置 / 执行 / 清理),或按团队分组(用户 / 系统 / 外部)
级别 3:部分内部细节
每个部分内的证据工件、代码片段和具体示例。这里是教育价值所在。
示例:在“后端”部分内,您可能会展示实际的 API 响应格式,而不仅仅是一个标有“API 响应”的框
对于综合性图表,力求包含所有三个级别。 概要提供上下文,部分进行组织,细节进行教学。
差与好
| 差(展示) | 好(论证) |
|---|---|
| 5 个带标签的等大框 | 每个概念都有一个反映其行为的形状 |
| 卡片网格布局 | 视觉结构与概念结构相匹配 |
| 装饰文本的图标 | 本身就是意义的形状 |
| 所有内容使用相同的容器 | 每个概念使用不同的视觉词汇 |
| 所有内容放在框内 | 自由浮动文本与选择性容器 |
简单 vs. 全面(知道您需要哪种)
| 简单图表 | 全面图表 |
|---|---|
| 通用标签:“输入” → “处理” → “输出” | 具体:展示输入/输出的实际样貌 |
| 命名框:“API”、“数据库”、“客户端” | 命名框 + 实际请求/响应的示例 |
| “事件”或“消息”标签 | 带有来自规范的真正事件/消息名称的时间线 |
| “UI”或“仪表板”矩形 | 展示实际 UI 元素和内容的模型 |
| 大约 30 秒解释 | 大约 2-3 分钟的教学内容 |
| 观众了解结构 | 观众了解结构和细节 |
简单图表适用于抽象概念、快速概览或当观众已经了解细节时。全面图表适用于技术架构、教程、教育内容,或当您希望图表本身进行教学时。
容器 vs. 自由浮动文本
并非每段文本都需要一个形状包裹。 默认使用自由浮动文本。仅在容器有明确目的时才添加。
| 何时使用容器... | 何时使用自由浮动文本... |
|---|---|
| 它是某个部分的焦点 | 它是标签或描述 |
| 需要与其他元素进行视觉分组 | 它是支持性细节或元数据 |
| 箭头需要连接到它 | 它描述附近的东西 |
| 形状本身承载意义(决策菱形等) | 仅排版就足以创建层次结构 |
| 它代表系统中一个独特的“事物” | 它是章节标题、副标题或注释 |
排版作为层次结构:使用字号、字重和颜色来创建视觉层次,无需框。一个 28px 的标题不需要矩形包裹。
容器测试:对于每个带框的元素,问自己:“这作为自由浮动文本是否可行?” 如果可以,移除容器。
设计过程(在编写 SVG 之前执行)
步骤 0:评估所需深度
首先,确定这是否需要是:
- 简单/概念性:抽象形状、标签、关系(思维模型、理念)
- 全面/技术性:具体示例、代码片段、真实数据(系统、架构、教程)
如果是全面性:首先进行研究。查找实际的规范、格式、事件名称、API。
步骤 1:深入理解
阅读内容。对于每个概念,问:
- 这个概念做了什么?(而不是它是什么)
- 概念之间存在哪些关系?
- 核心的转换或流程是什么?
- 人们需要看到什么才能理解它?(而不仅仅是读到)
步骤 2:将概念映射到模式
对于每个概念,找到反映其行为的视觉模式。每种模式的草图和使用说明见 references/visual-patterns.md。
| 如果概念... | 使用此模式 |
|---|---|
| 产生多个输出 | 扇出(从中心发出的径向箭头) |
| 将多个输入合并为一个 | 汇聚(漏斗,箭头合并) |
| 具有层次/嵌套 | 树形(线 + 自由浮动文本) |
| 是一系列步骤 | 时间线(线 + 点 + 自由浮动标签) |
| 循环或持续改进 | 螺旋/循环(返回起点的箭头) |
| 是抽象状态或上下文 | 云(重叠的椭圆) |
| 将输入转换为输出 | 装配线(之前 → 过程 → 之后) |
| 比较两个事物 | 并排(带对比的平行) |
| 分成不同阶段 | 间隙/中断(各部分之间的视觉分隔) |
步骤 3:确保多样性
对于多概念图表:每个主要概念必须使用不同的视觉模式。没有统一的卡片或网格。
步骤 4:勾勒流程
在编写标记之前,在脑海中追踪视线如何在图表中移动。应该有一个清晰的视觉故事线。
步骤 5:构建 HTML/SVG
现在才从 references/starter.html 开始编写文件。在 20px 网格上布局,并在放置任何内容之前写下关键坐标(列、行、节点中心)。请参阅下文了解如何处理大型图表。
步骤 6:渲染与验证(强制性)
编写文件后,您必须运行渲染-查看-修复循环,直到图表看起来正确。这不是可选的——请参阅下面的渲染与验证部分了解完整流程。
大型/综合性图表策略
对于综合性或技术性图表,一次构建 SVG 的一个部分。 不要一次性编写整个文件。一个综合性图表可能超出每次输出的限制,即使它能放得下,每次一个部分也能让每个部分得到真正的关注。
逐部分工作流程
阶段 1:构建每个部分
- 从
references/starter.html创建文件:设置<title>、viewBox,并添加第一个部分。 - 每次编辑添加一个部分,每个部分放在自己的
<g id="sec-...">中。思考该部分的布局、间距,以及它如何与已有内容连接。 - 使用描述性 id(
data-node="trigger"、id="sec-backend"),以便跨部分的边易于阅读:data-from="trigger" data-to="router"。 - 在 svg 顶部的 HTML 注释中保存坐标计划(列 x、行 y、部分边界)。后面的每个部分和每条边都从中读取,这是无需猜测即可修复间距的方法。
阶段 2:整体审查
通读整个文件并检查:
- 每个
data-from/data-to是否指向一个存在的data-node? - 间距是否平衡,还是某些部分拥挤而另一些稀疏?
viewBox是否包含所有内容,并留有大约 40px 的边距?
阶段 3:渲染与验证(见下文)。
部分边界
...