SVG 书籍配图设计规范
书籍/正式文章 SVG 配图的视觉设计系统。所有生成的 SVG 必须遵循本规范。
一、画布
- viewBox:宽 720px 固定,高度按内容裁剪(v1.7.1)——viewBox="0 0 720 H",H = 内容底边最大 y + 40px;不再固定 720×400 / 1.8:1(固定 400 会让内容少的图底部留白过大、图注间距不一致;用户 2026-06-30 反馈 SVG 转出 PNG 下边缘离图注间距忽大忽小,根因即此)。
- 稳定图身份:v1.8.9+ 新生成/新落稿 SVG 的根元素必须有项目内唯一
data-figure-id="fig-chNN-sN-NN"。值限 1–128 位字母、数字、点、下划线或连字符;模板fig-template-*只作示例,落稿前替换。下游 review / render inventory 以此绑定跨轮证据,缺失或重复均失败。 - 背景:透明背景(硬约束)——新生成图不画任何背景矩形、不设
background、不在<svg>根设底色。视觉区分由内部模块填充色承担(详见 §5)。 - 安全边距:左右 40px、顶部 40px;底部 = H − 内容底边(即 40px,由 viewBox 裁剪保证)
- 有效绘图区:宽 640px(x: 40–680);高度按内容(y 从 40 到 H−40)
- 渲染:源 SVG 不嵌字体样式;正式预览运行
python3 scripts/render_svg.py in.svg out.png,由 wrapper 读取assets/render-fonts.css并固定宽 720(不指定高度,按 viewBox 比例);高 DPI 的svg2png.js注入同一 CSS。禁止用裸渲染器输出作验收证据。
关于老图:main 上既有 34 张白底单色 SVG 是历史产物(
<rect width="720" height="400" fill="#FFFFFF"/>),保持稳定不回改(作者 2026-06-20 确认)。新规则只管新生成图:透明底 + 内部模块多色柔和区分;data-figure-id同样不要求在本次升级中回填历史书稿。透明底与白底是两种不同做法——老图不强制改透明。
二、印刷物理尺寸推算
SVG viewBox 是虚拟坐标,实际印刷尺寸由排版决定。本节从物理尺寸反推所有参数。
中国常见书籍开本
| 开本 | 成品尺寸 | 版心宽度 | 版心高度 | 通栏配图宽 | 典型用途 |
|---|---|---|---|---|---|
| 16开 | 170×240mm | ~115mm | ~190mm | 115mm | 技术书、教材(本 skill 主目标) |
| 大32开 | 140×203mm | ~95mm | ~160mm | 95mm | 通俗读物、商业书 |
| 32开 | 130×184mm | ~85mm | ~150mm | 85mm | 口袋书 |
| A5 | 148×210mm | ~100mm | ~170mm | 100mm | 国际标准 |
换算公式
物理尺寸(mm) = SVG单位 × (印刷宽度mm / 720)
物理尺寸(pt) = 物理尺寸(mm) / 0.3528以主目标 16开(115mm 通栏)为例:1 SVG单位 ≈ 0.160mm
中文印刷可读性标准
| 等级 | 字号 | 物理尺寸 | 用途 |
|---|---|---|---|
| 正文 | 五号 10.5pt | 3.70mm | 书籍正文 |
| 图注 | 小五号 8pt | 2.82mm | 图注、标签最低线 |
| 辅助 | 7pt | 2.47mm | 仅限纯英文/数字,中文不建议 |
| 危险 | <7pt | <2.47mm | 中文不可辨认 |
关键换算表(16开 115mm 通栏)
1 单位 = 0.160mm
| SVG字号 | 物理尺寸 | pt值 | 可读性 |
|---|---|---|---|
| 24px | 3.84mm | 10.9pt | 舒适,适合图标题 |
| 22px | 3.52mm | 10.0pt | 舒适,适合层标签 |
| 20px | 3.20mm | 9.1pt | 良好 |
| 18px | 2.88mm | 8.2pt | 刚好过 8pt 下限 → 节点标签最低字号 |
| 16px | 2.56mm | 7.3pt | 偏小,仅限英文/数字子标签 |
| 14px | 2.24mm | 6.4pt | 中文不可读 |
| 12px | 1.92mm | 5.4pt | 完全不可读 |
不同开本的标签最低字号
| 开本 | 印刷宽度 | 达到 8pt(2.82mm) 所需 | 达到 9pt(3.17mm) 所需 |
|---|---|---|---|
| 16开 | 115mm | 18px | 20px |
| 大32开 | 95mm | 22px | 24px |
| 32开 | 85mm | 24px | 27px |
结论:16开下 18px 是节点标签的硬底线。大32开需要 22px。
三、字体规格(基于 16开 115mm 推算)
| 用途 | SVG字号 | 物理(16开) | pt值 | 字重 | 颜色 |
|---|---|---|---|---|---|
| 图标题 | 22px | 3.52mm | 10pt | 600 | #2D3436 |
| 层标签(layer 模板) | 20px | 3.20mm | 9.1pt | 600 | #2D3436 |
| 节点标签 | 18px | 2.88mm | 8.2pt | 400 | #2D3436 |
| 子标签/第二行 | 16px | 2.56mm | 7.3pt | 400 | #636E72 |
字体族单一权威源:assets/render-fonts.css。字体栈只在该文件维护;源 SVG、生成器、模板和渲染脚本均不得复制字体栈。Markdown/Obsidian 预览继承宿主字体,正式导出统一由 scripts/render_svg.py / scripts/svg2png.js 加载该 CSS。
标签长度限制(18px 标签,汉字宽度约为字号的 1.05 倍):
- 18px 标签:最多 8 个汉字(约 151px)或 10 个英文字符
- 16px 子标签:最多 10 个汉字(约 168px)
- 超出时缩写或拆成子标签
中文换行:使用多个 <text> 元素:
<!-- 主标签 + 子标签 -->
<text x="360" y="195" text-anchor="middle" font-size="18" fill="#2D3436">法律评注</text>
<text x="360" y="214" text-anchor="middle" font-size="16" fill="#636E72">知识库最佳载体</text>行间距:两行文字 y 坐标差 18-20px。
四、节点尺寸(基于 18px 标签推算)
18px 单行中文标签 + 上下内边距:
节点高度 = 18px(文字) + 10px(上内边距) + 12px(下内边距) = 40px 最低
推荐 44px(留余量给视觉呼吸)
两行标签节点 = 第一行 18px + 间距 18px + 第二行 16px + 上下内边距 = 约 56px| 元素类型 | 宽度 | 高度 | 能放多少字 |
|---|---|---|---|
| 标准节点 | 140–160px | 48px | 8 字单行(18px) |
| 宽节点(layer 内) | 200–240px | 48px | 12 字单行 |
| 紧凑节点(hub 外围) | 120px | 44px | 6 字 |
| 两行节点 | 140–160px | 56px | 8字+8字 |
物理尺寸验证(16开 115mm):
- 标准节点 160×48px → 物理 25.6×7.7mm → 高度 7.7mm 看起来合适
- 紧凑节点 120×44px → 物理 19.2×7.0mm → OK
- 4 个标准节点水平排列:4×140 + 3×26.7 = 640px → 正好占满 640px 有效宽度
节点尺寸下限
任何节点不得小于:
- 宽 100px(物理 16mm,放 5 个 18px 汉字)
- 高 40px(物理 6.4mm)
五、颜色系统
5.0 配色总则(v1.7.0 起条件化拆分,按模板语义分类;v1.8.8 起补「蓝色焦点 + 文字二档」通用规则)
适用范围:本节适用于 v1.7.0 及之后新生成的图。main 上既有图(含 v1.5.0-v1.6.x 生成的)保持稳定不回改——只让新生成的 layer/tree/金字塔模板走新规则。
supersede 说明:v1.5.0 DEC-010 的"内部模块多色"规则仍适用于多样性关系类模板(flow/matrix/hub/cycle),但对层级归属类模板(layer/tree/金字塔)不适用——本版为后者引入"同色相灰度梯度"新规则。详见 DEC-012。
项目级规范优先(v1.8.6 起,防漂移):生成配图前先读项目的
figures/FIGURES-OUTLINE.md(或等价配图大纲/风格规范)。若该书在其中锁定了 canonical 配色,从其规定——本 skill 的 P1-P8 / G1-G4 是通用兜底,不得与项目 canonical 冲突。这是防止"不同批次补图风格漂移"的硬约束。「蓝色焦点 + 文字二档」通用规则(v1.8.8 起,DEC-020):即便项目未锁定 canonical,本 skill 默认推荐以下三条作为通用配色纪律(跨项目,非项目指针):
- 文字色统一深灰二档:图内所有文字仅允许主标签
#2D3436、子标签#636E72(见 §5.1);深色填充(L* ≤ 50)模块上文字用#FFFFFF/#EDF2F7(见 §5.5.1)。不再使用#1A202C/#4A5568等四档深灰——收敛到二档,消除同图深浅灰混用的不一致。- 蓝色(
#2C5282/#1A365D)只用于焦点节点的填充 / 描边,不得作文字 fill:当项目 canonical 锁定某主色(如#2C5282蓝)为视觉标识时,该色只出现在焦点节点(起点 / 终点 / 核心 / 输出)的填充或描边上以色块承载强调,禁止把它当文字fill染字(蓝字与深灰字混用会导致层次不清)。文字强调改用字重(600/700)或字号,不用色相。- 每张结构 / 流程图至少 1 个焦点节点:layer / tree / flow / hub / cycle / matrix 等结构性模板应至少有 1 个焦点节点(用 canonical 主色或更深一档填充 / 描边)承载层次,反对纯灰平铺(无焦点 → 平、无重点)。纯并列清单(如五要素列表)无自然焦点时,用灰阶递进 + 边框粗细区分层次,逐图评估是否补焦点色。
这三条是对 §5.1(文字色)、§5.5.1(深底浅字)的规则化提级——把它们从"默认假设"升级为"生成期硬约束 + review checklist 勾选项",防"语法合法但配色不一致 / 无焦点"的漂移。
两条总原则:
- 透明背景,绝不加底色:SVG 根标签和内容里不画任何背景矩形、不设
background、不写画布底色。视觉空间由排版(纸/页面)提供,配图叠上去就是透明底。 - 按模板语义分类配色——按"内部模块之间的语义关系"选择调色板类别:
| 模板类型 | 语义关系 | 调色板类别 | 规则 |
|---|---|---|---|
| layer / tree / 金字塔 | 层级归属(同族,递进) | §5.2b 灰度梯度 G1-G4 | 同色相 + 不同明度,顶层最浅、底层最深 |
| flow / matrix / hub / cycle | 多样性区分(不同对象) | §5.2 模块色 P1-P8 | 多色相 + 柔和去饱和,相邻模块不同色 |
- 颜色不是唯一区分手段:仍用形状、位置、线型、边框粗细、填充深浅协同区分元素——黑白打印或 CMYK 偏色下仍可辨(继承自旧规范)。
5.1 基础中性色(文字 / 边框 / 连线 · v1.8.8 收敛二档)
| 用途 | 色值 | 说明 |
|---|---|---|
| 深灰文字 / 边框 / 连线 | #2D3436 |
主文字色(统一收敛),对任何柔和模块填充色对比 ≥ 7:1 |
| 中灰子标签 | #636E72 |
辅助文字(统一收敛) |
| 浅灰辅助线 | #B2BEC3 |
分隔线、弱连接 |
文字色硬规则(v1.8.8 起,DEC-020):
- 图内所有文字仅允许深灰二档:主标签
#2D3436、子标签#636E72;深色填充(L* ≤ 50)模块上文字用#FFFFFF/#EDF2F7(见 §5.5.1)。- 不再使用
#1A202C/#4A5568(收敛到二档,消除同图四档深灰混用的不一致——这两档在过去版本偶现于标题/强调位,现统一到#2D3436)。- 禁止以项目 canonical 主色(如
#2C5282/#1A365D蓝)作为文字 fill——主色只出现在焦点节点的填充 / 描边上(§5.0 第 2 条)。文字强调改用字重(600/700)或字号,不用色相。文字色统一用深色(
#2D3436/#636E72)保证在任意柔和模块填充色上都清晰可读。不要把文字写成柔和色(浅蓝/浅绿等)或项目主色——文字可读性优先。
5.2 预定义柔和模块色调色板(8 组,新生成图选用)
每组 = 一组柔和的「模块色」(5-6 个去饱和、低饱和、纸面友好的色),用于区分 SVG 内部不同模块 / 分支 / 方向 / 层级。一组内所有色都是柔和(去饱和)的同明度档(避免某个模块过亮/过暗抢视觉重心),色相彼此拉开但都压住饱和度。
关键概念转变:上一版"调色板"是「画布浅底 + 强调色」二元结构(已被废弃)。本版调色板是「内部模块多色」结构——一组给 5-6 个模块色,不再有"画布底"这一列。
| 编号 | 色组名 | 模块色(用于区分内部模块,去饱和柔和) | 适用主题 |
|---|---|---|---|
| P1 | 雾蓝系 | #D6E4F0 #C5D9E8 #B8CFE0 #DCE8F2 #C9DCEC #E0EBF4 |
科技、AI、数据、系统架构 |
| P2 | 浅青系 | #C0E8E0 #B5DDD4 #A8D2C9 #CFEDE5 #BCDFD6 #D3EFE9 |
工具、流水线、协作 |
| P3 | 嫩绿系 | #C8EBC8 #BDDFBD #B0D2B0 #D4EED4 #C3E3C3 #D8F0D8 |
成长、流程闭环、正向结果 |
| P4 | 暖米系 | #E8D8C0 #DCCAB0 #D0BEA0 #EDDFC8 #E0D0B5 #F0E2CC |
法律、正式、风控、合规 |
| P5 | 浅紫系 | #DCC8E8 #CFBDDC #C2B2D0 #E2D0EC #D5C3DF #E7D6F0 |
思考、概念、抽象、方法论 |
| P6 | 浅粉系 | #F5D8E0 #E8CBD2 #DCBEC6 #F8DFE5 #EFD2D8 #FAE4EA |
情感、用户视角、体验 |
| P7 | 暖灰系 | #E8DFD0 #DCD3C4 #D0C7B8 #EDE5D7 #E0D8C9 #F0E9DC |
叙事、随笔、文化 |
| P8 | 混合柔和系 | #D6E4F0(蓝) #C8EBC8(绿) #E8D8C0(米) #DCC8E8(紫) #F5D8E0(粉) |
多方向/多分支对比(一图内天然多色,无需另选) |
使用约定:
- 每张图选 1 组(P1-P7)或用 P8 混合系;同图内部模块按"模块 1 取色 1、模块 2 取色 2…"依次分配,让相邻模块不同色。
- 模块色用于内部矩形 / 卡片 / 区块的
fill:<rect fill="{模块色}" stroke="#2D3436" stroke-width="2"/>。 - 文字色始终
#2D3436/#636E72,不写浅色。 - 绝对不画背景矩形:不要
<rect width="720" height="400" fill="..."/>。SVG 直接画模块(透明底由页面/纸面提供)。 - 模块色应覆盖内部所有矩形/区块;连线/箭头/marker 用深灰
#2D3436(或取该模块对应的更深一档色做箭头),不抢戏。
5.2b 预定义单色灰度梯度调色板(v1.7.0 新增,专用于层级类模板)
用途:layer / tree / 金字塔模板——表达"层级归属(上层包下层 / 父节点辖子节点)"语义时,颜色用同色相的不同明度档营造层次感,而不是不同色相混搭。
关键概念:与 §5.2 的 P1-P8 多色区分不同——这里一组给的是5 档明度(同色相),按"层级 1 取档 1、层级 2 取档 2、…"分配,色相统一、仅明度变化。一图内部同一色相,靠明度差建立层次。
| 编号 | 色组名 | 档位 1(最浅) | 档位 2 | 档位 3(中) | 档位 4 | 档位 5(最深) | 适用主题 |
|---|---|---|---|---|---|---|---|
| G1 | 蓝灰梯度 | #F0F4F8 |
#D6E4F0 |
#B8CFE0 |
#9AB8D0 |
#7CA0BC |
科技、AI、数据、系统架构(冷调中性) |
| G2 | 法律米梯度 | #F4ECDC |
#E8D8C0 |
#D8C4A4 |
#C4AE88 |
#B8A282 |
法律、合规、正式文书(暖调,本 skill 主推荐) |
| G3 | 暖灰梯度 | #F0EDE8 |
#E8DFD0 |
#DCD3C4 |
#D0C7B8 |
#C4BBAC |
叙事、随笔、文化、通用文档 |
| G4 | 蓝梯度 | #E8F0F8 |
#C5D9E8 |
#A0BED4 |
#7CA0BC |
#5A82A4 |
系统、架构、技术(冷调明度跨度大) |
使用约定:
- layer / tree / 金字塔模板强制使用 G1-G4 中任一组——禁止误用 P1-P8 多色区分(把同色系层画成不同色相)。
- 同图层级数 ≤ 5 时:层级 1(最顶层)取档位 1(最浅),层级 5(最底层)取档位 5(最深),中间按"层级 N → 档位 N"对应。
- 同图层级数 > 5 时:拆成两张图,或取档位 1-5 的子集(按层级数等距取)。
- 树/金字塔同理:根节点用档位 1、子节点用档位 2、叶子用档位 3(金字塔 3 层变体)或更深档。
- 模块色用于内部矩形 / 卡片 / 区块的
fill:<rect fill="{档位 N 色}" stroke="#2D3436" stroke-width="2"/>。文字色统一#2D3436。 - 明度跨度的硬约束:档位 1(最浅)模块色明度 L* ≥ 90,保证深色文字 vs 模块填充色对比 ≥ 4.5:1(WCAG AA 正常文本);档位 5(最深)模块色明度 L* ≥ 55(最深档 G2-5 =
#B8A282,L* ≈ 56,对比度 ≥ 4.5:1,刚好过线)。若发现最浅档不够浅 / 最深档过深,按 G2/G4 切换组或微调档位。 - 绝对不画背景矩形:同 §5.2。
- 连线/箭头/marker 用深灰
#2D3436,不抢戏。
5.3 打印友好约束(CMYK / 纸面可读硬约束)
透明背景下的对比度口径:对比度是 文字色 vs 所在模块的填充色(不是 vs 画布底——没有画布底)。因此模块填充色必须足够浅(去饱和),让深色文字落在上面仍 ≥ 4.5:1。
| 约束 | 阈值/规则 | 验证方法 |
|---|---|---|
| 文字对比度 | 文字色(#2D3436)与所在模块填充色亮度对比 ≥ 4.5:1(WCAG AA 正常文本);标题/节点标签(≥18px)≥ 3:1(AA 大文本) |
WebAIM Contrast Checker(输入文字色 vs 模块填充色) |
| 相邻模块区分度 | 相邻模块(同行/同列/直接连接的两个矩形)填充色之间亮度差 ≥ 10% 且色相差可辨;非相邻可同色 | 肉眼:相邻模块一眼能分开 |
| 模块填充明度上限 | 所有模块色明度 L* ≥ 80(约 #E0E0E0 以上偏浅),保证深色文字对比 |
调色板内 8 组已统一标定到柔和档 |
| 禁高饱和荧光 | 禁 #FF0000/#00FF00/#FFFF00/#FF00FF 等纯三原色高饱和;调色板内已统一去饱和 |
不使用调色板外的色 |
| 红绿不并置做区分 | 红绿对比在色盲+黑白下都失效;若必须红绿,靠填充深浅+边框粗细补区分 | 避免纯红与纯绿相邻做唯一区分手段 |
| CMYK 转换不偏色 | 所有色值在 sRGB 空间,转 CMYK 时不出现明显色相漂移(蓝→紫、黄→棕) | 出片前用 Photoshop/Ghostscript 转 CMYK 预览 |
| 黑白降级可辨 | 去色(grayscale)后,相邻模块灰度差 ≥ 10%;文字与模块填充灰度差 ≥ 15% | convert in.svg -colorspace Gray out.png 预览 |
打印友好自检脚本(生成后必跑一次,灰度预览 + 目检):
python3 scripts/render_svg.py in.svg out.png
# 灰度预览(模拟黑白印刷)
convert out.png -colorspace Gray gray.png
# 肉眼看 gray.png:模块彼此可分、文字清晰 = 通过5.4 颜色语法约束(与既有语法门禁兼容,必读)
硬约束:颜色只能通过
fill/stroke属性内联到具体元素上。源 SVG 禁止<style>与任何元素的style=;字体只由外部assets/render-fonts.css管理。严禁以下写法:
<!-- ❌ 禁止 1:用 <style> 块定义颜色类 -->
<style>
.node-blue { fill: #D6E4F0; stroke: #2C5282; }
</style>
<!-- ❌ 禁止 2:在 <svg> 开标签上写 font-family -->
<svg viewBox="0 0 720 400" font-family="...">
<!-- ❌ 禁止 3:在任一元素写 style= 或复制字体栈 -->
<text style="fill:#2D3436" font-family="...">节点</text>
<!-- ❌ 禁止 4:用 CSS 变量 / currentColor / class 引用色 -->
<rect class="node-blue"/>
<!-- ❌ 禁止 5:画背景矩形当画布底(透明背景硬约束) -->
<rect width="720" height="400" fill="#F0F4F8"/>正确写法:每个元素的 fill/stroke 直接写色值;不画任何背景。
<!-- ✅ 正确:透明背景 + 属性内联模块色 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400" data-figure-id="fig-ch03-s2-01">
<!-- 注意:没有背景矩形!直接画模块 -->
<rect x="40" y="176" width="140" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="110" y="205" text-anchor="middle" font-size="18" fill="#2D3436">识别场景</text>
<!-- 相邻模块用同组不同色 -->
<rect x="207" y="176" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="277" y="205" text-anchor="middle" font-size="18" fill="#2D3436">梳理流程</text>
</svg>为何如此:
- 透明背景:作者 2026-06-20 明确要求——配图叠在排版纸面上,底色由书页提供,不应自带底色。
- 属性内联(不用
<style>/style=/ 内嵌 font-family):Obsidian/Markdown 内嵌渲染时存在已知不兼容(嵌套引号、样式作用域——见 memoryfeedback_svg_embed_syntax)。颜色保持fill/stroke属性,字体在导出时从受控外部 CSS 注入,保证源文件兼容与正式导出稳定不冲突。
5.5 可视友好化配色与渲染禁忌(v1.8.7 新增,硬约束)
为什么加这节:§5.1-§5.4 的"文字色统一深灰
#2D3436/#636E72、模块色柔和浅"是默认假设「模块填充是浅色、文字是深色」的搭配。但生成图时若模块用深色填充(项目 canonical 锁定的#2C5282深蓝做强调、#1A202C近黑、tree 根节点加深档#5A82A4、表头#2C5282等),深灰文字落上去会与深底融为一片不可读。此外图 7-13 / 8-3 / 6-7 等若干图出现"框画了但<text>没写 / 文字 fill 与背景同色 / 文字被裁出框外"的视觉缺失。本节把这些"看似显然、却反复发生"的渲染事故固化为硬约束。来源:作者 review T134 决策清单嵌图时实测发现(图 7-6 箭头落点错位、图 11-8 字色不可辨、图 11-9 深蓝背景配深色字、图 7-13/8-3/6-7 框内文字缺失),均属"语法 xmllint 通过、但视觉渲染失败"的典型。
5.5.1 深底浅字(硬约束)
凡 <rect> / <circle> / <path> 等模块填充色为深色(明度 L* ≤ 50,典型如 #2C5282/#1A365D/#1A202C/#2D3748/#4A5568,以及 G1-G4 梯度的最深档 #5A82A4/#7CA0BC 与项目 canonical 强调色),其内部所有 <text> 的 fill 必须用浅色:
<!-- ✅ 深蓝表头 + 白字 -->
<rect x="40" y="40" width="640" height="40" rx="6" fill="#2C5282"/>
<text x="360" y="66" text-anchor="middle" font-size="18" fill="#FFFFFF">表头标题</text>
<!-- ❌ 禁止:深底深字(融合不可读) -->
<rect ... fill="#2C5282"/>
<text ... fill="#2D3436">表头标题</text>允许的浅色文字 fill(深底专用):
#FFFFFF(纯白,深底首选)#EDF2F7/#F7FAFC(极浅灰白,柔和档)#E2E8F0(浅灰白)
绝对禁止:深色填充(L* ≤ 50)的模块上写 #2D3436/#636E72/#4A5568 等深灰字——视觉对比度 < 2:1,不可辨。
判定快捷法:模块
fill转灰度后 <#7F7F7F(中灰)即判深底,文字必走浅色。
5.5.2 字色对比度(与所在模块填充色,WCAG 视觉判断)
通用规则(覆盖浅底与深底):<text> 的 fill 与其所在区域(最贴近的、含该 <text> 几何中心的 <rect>/<circle>/<path> 的 fill,或 SVG 根透明背景外的页面底)的亮度对比度 ≥ 4.5:1(WCAG AA 正常文本);≥18px 大文本 / 图标题 ≥ 3:1。
- 浅底(柔和模块色 P1-P8、灰度梯度 G1-G4 档 1-4,L* ≥ 55):文字用深灰
#2D3436/#636E72(默认 §5.1 规则)。 - 深底(L* ≤ 50):文字必走 §5.5.1 的浅色
#FFFFFF/#EDF2F7。 - 临界区(L* ≈ 50-55,如 G2-5
#B8A282、G4-4#7CA0BC):两种方向都需测对比度,无脑深灰可能 < 4.5:1——用 WebAIM Contrast Checker 验证,不够则换白字或换更浅的模块色档。
验证手段:把文字 fill 与模块 fill 输入 WebAIM Contrast Checker,看对比度数值;或在 review-checklist §④ 视觉目检里用多模态渲染 PNG 直接看(深底深字在渲染图上一眼可辨)。
5.5.3 箭头 marker 与落点(与 §六「箭头」协同,硬约束)
此节为 §六「箭头」落点对齐规则的渲染友好化补强,专防图 7-6 类「marker 落点穿框 / 悬空 / 方向错」问题。
- marker 必带
markerUnits="userSpaceOnUse"+orient="auto",单id="arrow":禁止arrV/arrF/arrG/arrT/arrR/arrL等多方向自造 marker(详见 §六 / DEC-011)。一个id="arrow"+orient="auto"通吃水平 / 垂直 / 斜向。 - 落点公式(硬算,不靠目测):
- 线起点
x1/y1= 源框边缘 + 4px 间隙 - 线终点
x2/y2= 目标框边缘 − 4px 间隙(让 marker 箭头尖端落在目标框边外 4px,不穿框、不悬空) - 例:源框右
x=180、目标框左x=207→x1=184、x2=203;可见线长 =203−184−10(markerWidth) = 9px纯线 + 10px 箭头
- 线起点
- 斜向 / 垂直箭头同理:tree 的父→子斜线、hub 的中心→外围放射线、cycle 的弧形连线,终点都落在目标框边外 4px——禁止 marker 落在框内(穿框)、禁止离框 > 8px(悬空)。
- 方向自洽:
<line x1 y1 x2 y2>的(x2,y2)是箭头尖端方向;写反了箭头会指错(图 7-6 即此问题)。生成后核对每条 line 的 (x2,y2) 是否落在目标节点,不在则改 x1/y1 ↔ x2/y2 对调。
5.5.4 文字完整性(生成后核实每个框的 <text> 齐全)
此节防图 7-13 / 8-3 / 6-7 类「框画了但文字没写 / 文字 fill 与背景同色 / 文字坐标出框外」的视觉缺失——这些图 SVG 语法合法、xmllint 通过,但渲染出来框里是空的或文字"看不见"。
生成 SVG 后逐框核实(建议自动化 grep + 多模态目检双保险):
- 每个
<rect>/<circle>/<path>节点都有对应<text>:grep<rect计数与<text计数对照(一个框允许 0 个 text——纯装饰/连线点,但有标题语义的节点框不可缺 text)。重点查:表头栏、layer 各层标签、tree 叶节点、hub 外围节点、matrix 单元格。 - 文字 fill 与所在模块 fill 不同色:若文字
fill="#2C5282"落在fill="#2C5282"的模块上(或同色系深浅相差 < 10%),渲染时融为一片"看似缺失"——必须改文字 fill(按 §5.5.1/5.5.2 走浅或深)。 - 文字坐标在框内:
<text x y>的几何中心应在所属<rect x y width height>范围内(x 在 [rect.x+8, rect.x+rect.w−8]、y 在 [rect.y+12, rect.y+rect.h−8])。文字 x/y 算错(如复用了上一节点坐标)会"飘出框外",渲染看似缺字。 - 文字不空:
<text>...</text>之间不能是空串 / 纯空白 / 占位符(如???/TODO/XXX)——生成器漏填字段时常见。 - 多模态渲染目检兜底:渲染为 PNG 后用多模态模型看,逐框核对"框里是否有可读文字"(与 SVG 源码的 text 列表对照,源码有但渲染看不见 = 配色或坐标问题;源码就没有 = 生成器漏写)。
六、形状规范
圆角矩形
模块填充色从 §5.2 调色板取值(以下示例用 P1 雾蓝系的两个相邻模块色;边框统一深灰):
<!-- 模块 1 -->
<rect x="40" y="40" width="150" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<!-- 相邻模块 2,同组不同色 -->
<rect x="207" y="40" width="150" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>- 圆角
rx="6" - 边框
2px(普通)、3px(强调根节点/终止节点) - 线宽
2px在 16开下物理 0.32mm,印刷可见 - 相邻模块同色组取不同色,避免色块连成一片
容器包含语义(v1.8.10+,与 writing-reviewer v0.16+ 对齐)
shape 完全包含另一 shape 既可能是“面板承载信息卡”的正常构图,也可能是坐标写错造成的遮挡。不要靠几何猜意图:默认先把包含/重叠当作布局缺陷修坐标;只有外层 shape 确实承担容器语义时,才使用唯一的 candidate-bound 窄声明。
<!-- 合法:外层卡片真实承载内层信息卡,意图与身份均可审计 -->
<rect id="outer-card" x="40" y="40" width="220" height="160" rx="6"
fill="#EDF2F7" stroke="#2D3436" stroke-width="2"
data-overlap-role="container"
data-overlap-note="外层卡片承载内层信息卡"/>
<rect x="60" y="70" width="180" height="90" rx="6"
fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>data-overlap-role只允许精确值container;禁止decoration、background或任意自造 role,禁止用data-allow-overlap给候选 SVG 自发放行。- 声明只能放在 writing-reviewer 可测量的外层 area shape:
rect/circle/ellipse/polygon/path;不要标在g、text、连线或根svg上。 - 外层必须同时有:①单张 SVG 内唯一、无 namespace 的稳定安全
id(1–128 位字母、数字、点、下划线或连字符,首位为字母或数字);②无 namespace、至少六字且含“承载/包含/容纳”等关系词的具体data-overlap-note,说明“外层承载什么”,不能只写“这里允许重叠/覆盖”;③可静态证明非透明的 hex/rgb/hslfill。none/transparent、零 alpha、命名色、inherit/currentColor/var()/url()等继承或无法在 producer 侧证明的值均不合格,opacity/fill-opacity必须大于 0。 data-overlap-note不能脱离containerrole 单独存在;x:id、x:data-overlap-note等 namespaced 假属性不会被浏览器getAttribute("id")识别,producer contract 直接拒绝。- 这组属性只表达候选源中的设计意图,不构成几何通过证据。producer contract 负责静态拒绝缺 id / 缺 note / 非法 role;writing-reviewer v0.16+ render gate 用真实浏览器几何判断是否实际包含,并把命中的 outer / inner / reason 写入 evidence。
- 装饰底纹若压住信息 shape,仍应改尺寸、层级或坐标;不得把装饰标成容器来消除 finding。容器声明长期未命中也不等于合规,应删除无效声明。
箭头
单个 marker 通吃所有方向(水平/垂直/斜向都用同一个 id="arrow",靠 orient="auto" 自动旋转,不要为每个方向另造 marker 如 arrV/arrF/arrG):
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="10" markerHeight="10" orient="auto" markerUnits="userSpaceOnUse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>为什么必须有
markerUnits="userSpaceOnUse":SVG 规范中markerUnits的默认值是strokeWidth(不是像素),意味着markerWidth="10"在stroke-width="2"时实际渲染为10 × 2 = 20px,在stroke-width="1.5"时为 15px——同一个规范下箭头大小随线宽飘忽。设userSpaceOnUse后 marker 尺寸固定为像素,与 stroke-width 解耦。详见 DEC-011。为什么必须用
orient="auto"单 marker:模型若为垂直 / 斜向箭头另造id="arrV"等并硬编码refX/refY,方向一多就乱(实测产物里出现arrV/arrF/arrG/arrT/arrR/arrL等 10+ 自创 marker,refX/refY 各种组合)。orient="auto"让 marker 沿<line>方向自动旋转,一个 id 通吃水平 / 垂直 / 斜向。
箭头落点对齐规则(箭头线 <line> 端点坐标怎么定):
| 位置 | 公式 | 例(flow 节点 1→2,源框右=180、目标框左=207) |
|---|---|---|
线起点 x1 |
源框边缘 + 4px 间隙 | 184(180+4) |
线终点 x2 |
目标框边缘 − 4px 间隙 | 203(207-4) |
| 实际可见线长 | x2 − x1 − markerWidth |
203 − 184 − 10 = 9px 纯线 + 10px 箭头 |
- 线起点 / 终点都留 4px 间隙:避免 marker 压源框、悬空目标框。
- 斜向箭头(tree、hub)按相同规则:线终点落在目标框边外 4px,避免扎进框内。
连线
- 实线:关键关系
- 虚线
stroke-dasharray="6,4":辅助/可选关系 - 线宽:主流程 2px、辅助 / 树形 / 中心辐射 1.5px(线宽变化不再影响箭头大小,因
markerUnits="userSpaceOnUse") - 语义优先:承载流程、反馈、复核、状态或条件含义的线不得为了“清爽”而删除;若线与文字或节点冲突,优先重路由、调整节点,再用简短的读者标签说明虚线 / 点线含义。只有无读者信息承载的调试线、辅助框或创作提示可删。
- 分叉连续:同一关系的主干与支线应视觉相接;除非刻意表达“未连接 / 条件未满足”,否则线段交界不留几何缝隙。分叉 / 汇合处应令读者一眼看出是一个关系还是多个关系。
- 层级连接紧凑:相邻层级箭头的可见线身优先为 16–32px;超过 48px 时,先压缩层间留白或重排节点,而不是用长箭头撑开画面。确有中间步骤、注释或跨组语义时可例外,但要让中间语义可见。
标签卡、编号与图例
- 标签卡:深色标题 + 浅色正文是同一张卡时,画一个外轮廓;标题栏仅保留上圆角,下边应为直角。避免用两个圆角矩形叠出下圆角、细缝或双层边框;需要时以单个
path画标题栏。 - 步骤编号:①②③或 1/2/3/4 置于卡内独立上方带,标题与说明在其下排列;编号不得压住文字。空间不足时先增加卡高 / 内边距,不缩小文字或强行叠放。
- 读者图例:实线、虚线、点线、问号、状态色等说明必须直接告诉读者“分别表示什么”;不把只用于作者、AI 或制图过程的协作提示放进最终图。
七、间距
| 规则 | 数值 | 物理(16开) | 理由 |
|---|---|---|---|
| 元素最小间距 | 24px | 3.8mm | 印刷下不糊在一起 |
| 层间垂直间距 | 40px | 6.4mm | 层次清晰 |
| 同层水平间距 | 28px | 4.5mm | 节点不挨 |
| 节点内边距 | 16px 水平, 12px 垂直 | 2.6mm / 1.9mm | 文字不贴边 |
八、印刷黑白兼容
- 颜色不是唯一区分手段:用形状、位置、线型区分
- 避免红绿对比(色盲+黑白都不行)
- 关键连线实线,辅助虚线
- 节点区分靠填充深浅 + 边框粗细,不仅靠颜色
九、元素密度上限
基于 680px 有效宽度和 48px 节点高度:
| 模板 | 最大节点 | 计算 | 最大字数/节点 |
|---|---|---|---|
| flow(水平) | 4 个 | 4×140+3×26.7=640 | 7-8 字 |
| flow(垂直) | 5 个 | 高度足够 | 12 字 |
| layer | 4 层 | 4×56+3×24=296 | 12 字 |
| matrix | 2列×3行 | 行高 60px×3+间距 | 8 字/格 |
| hub | 1+5 外围 | 半径 130px | 6 字 |
| tree | 1→3→5 | 叶子 5×120+4×20=680 | 6 字 |
| cycle | 4 个 | 矩形排列 | 8 字 |
超过上限时:拆成两张图,不要硬塞。
十、大32开适配说明
如果目标是大32开(95mm 通栏),1 单位 ≈ 0.132mm,所有尺寸需放大:
| 参数 | 16开值 | 大32开调整 |
|---|---|---|
| 节点标签 | 18px | 22px |
| 子标签 | 16px | 18px |
| 图标题 | 22px | 26px |
| 节点高度 | 48px | 56px |
| 最大 flow 节点 | 4 个 | 3 个 |
| 最大 hub 外围 | 5 个 | 4 个 |
建议大32开书籍直接缩小 viewBox(如 520×290),这样 18px 标签在 95mm 下达到 3.34mm = 9.5pt,舒适可读。
十一、图注格式
**图 N-X:图标题**十二、SVG 代码模板
注意:模板与生成器产物不得包含
<style>、style=或任何内嵌字体栈——这是 Obsidian/Markdown 兼容的源文件硬约束(见 §5.4)。预览继承宿主字体;正式导出统一由assets/render-fonts.css外部注入。不得把字体栈复制到<svg>或单个<text>。下面的fig-template-*只是模板身份,复制进书稿时必须改成项目内唯一fig-chNN-sN-NN。
模板 A:透明背景 + 内部多色(新生成图默认,示例用 P1 雾蓝系)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400" data-figure-id="fig-template-style-a">
<defs>
<!-- 箭头 marker:markerUnits=userSpaceOnUse 固定像素;orient=auto 单 marker 通吃所有方向 -->
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="10" markerHeight="10" orient="auto" markerUnits="userSpaceOnUse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>
</defs>
<!-- 注意:无背景矩形!透明底由书页提供 -->
<!-- 模块 1 -->
<rect x="40" y="176" width="140" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="110" y="205" text-anchor="middle" font-size="18" fill="#2D3436">识别场景</text>
<!-- 模块 2,同色组相邻模块用不同色 -->
<rect x="207" y="176" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="277" y="205" text-anchor="middle" font-size="18" fill="#2D3436">梳理流程</text>
<!-- 模块 3 -->
<rect x="373" y="176" width="140" height="48" rx="6" fill="#B8CFE0" stroke="#2D3436" stroke-width="2"/>
<text x="443" y="205" text-anchor="middle" font-size="18" fill="#2D3436">编写</text>
<!-- 箭头:线两端各留 4px 间隙 -->
<line x1="184" y1="200" x2="203" y2="200" stroke="#2D3436" stroke-width="2" marker-end="url(#arrow)"/>
<line x1="351" y1="200" x2="369" y2="200" stroke="#2D3436" stroke-width="2" marker-end="url(#arrow)"/>
</svg>模板 B:旧极简白底单色(仅 main 上 34 张历史图兼容,新图不再用)
历史产物保留:既有 34 张图带
<rect width="720" height="400" fill="#FFFFFF"/>白底,保持稳定不回改。新生成图一律走模板 A(透明底 + 内部多色)。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400" data-figure-id="fig-template-legacy-example">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>
</defs>
<rect width="720" height="400" fill="#FFFFFF"/>
<!-- 内容 -->
</svg>