Fork 文档站本身,十分钟内完成本地预览。
这是本节的多页打印视图。 .
组件总览
- 1: 提示块
- 2: 图片
- 3: 代码块
- 4: 标签页
- 5: 表格
- 6: 参数表
- 7: 步骤
- 8: 卡片
- 9: 文件树
- 10: 公式
- 11: Mermaid
- 12: PlantUML
- 13: 思维导图
- 14: Draw.io
- 15: ECharts
- 16: Infographic
- 17: 画廊
- 18: 徽章
- 19: 按键
- 20: 引用
- 21: Asciinema
这一栏回答一个问题:某个组件在 Markdown 里怎么写。每页的顺序相同:最简例子、逐步深入的例子、输出形态、参数表、限制。查语法见下面的速查表。
两种形态
组件的第一形态是 Markdown 语法本身:块引用、列表、表格、图片、围栏,加上紧跟其后的一行 {…} 属性。原生形态在 GitHub 与任意 Markdown 编辑器中仍然可读,Markdown 输出保留的也是源码。
原生形态表达不了的场景使用 shortcode:正文标签页、带块级描述的参数表、带图标与徽章的卡片、终端录像。规则有五条:
- 所有 shortcode 都写
{{</* 名字 */>}},只有{{%/* steps */%}}用%分隔符,因为它的正文是页面级 Markdown。 - 嵌套名字(
tab、card、field)只在各自的父 shortcode 里有效。 - 参数写错不会静悄悄降级,构建失败,报错带文件名与行号。
- 公开字符串参数(图注、标签、标题)一律是纯文本,不解析 Markdown。只有正文是 Markdown:
tab、card、field的正文,include引入的文件,以及 Book 的fig、tbl、eg正文。 - 页面没用到的组件不下发运行时。脚本按这一页实际用到的组件拼成一个包,打印、Markdown 与 RSS 输出不加载任何脚本。
站点前置配置
组件依赖三项 Goldmark 设置。克隆本站起步时它们已经配好,从零建站照抄以下片段:
renderer.unsafe: true:Goldmark 默认丢弃内容里的原始 HTML,关闭时组件正文里嵌套的 HTML 会消失。parser.attribute.block: true:属性行的总开关。关闭时{.steps}、{caption="…"}只是正文里的一行字符串。parser.wrapStandAloneImageWithinParagraph: false:独立成段的图片不再包进<p>,图片才能成为带图注的 figure,属性行才跟得上去。
个别组件另有前置条件:公式需要开启 Goldmark 的 passthrough,PlantUML 与 Draw.io 需要自建渲染服务,各页分别说明。完整的配置键见配置总览。
速查表
「形态」列的取值:原生 = Markdown 语法加属性行;围栏 = 带语言标记的代码围栏;shortcode = {{</* … */>}}。「运行时」列说明这个组件是否往页面上下发 JavaScript。
| 组件 | 一句话 | 最短写法 | 形态 | 运行时 |
|---|---|---|---|---|
| 提示块 | 把前提、警告与折叠说明从正文中分离 | > [!NOTE] | 原生 | 无 |
| 图片 | 图注、尺寸、缩放、编号与构建期图片处理 |  | 原生 | 需站点开关 |
| 代码块 | 高亮、标题、复制、折叠、行链接 | ```sh | 围栏 | 按页加载 |
| 标签页 | 同一件事的多个平台或语言版本 | 属性行 {tab="Linux"} | 原生 + shortcode | 按页加载 |
| 表格 | 普通表格,加满宽、矩阵、标题与编号 | {.full-width} | 原生 | 无 |
| 参数表 | 参数清单,带类型 / 必填 / 默认值芯片 | {.fields meta="type default"} | 原生 + shortcode | 无 |
| 步骤 | 有先后的流程 | {.steps} | 原生 + shortcode | 无 |
| 卡片 | 一组并列的去处 | {.cards} | 原生 + shortcode | 无 |
| 文件树 | 目录结构与对齐的注释列 | ```filetree | 围栏 | 按页加载 |
| 公式 | KaTeX 行内与块级公式 | $$ … $$ | 原生 | 按页加载 |
| Mermaid | 流程图、时序图、甘特图 | ```mermaid | 围栏 | 按页加载 |
| PlantUML | UML 图;需要自建渲染服务 | ```plantuml | 围栏 | 需站点开关 |
| 思维导图 | Markdown 列表变成思维导图 | ```markmap | 围栏 | 需站点开关 |
| Draw.io | 可回编辑的图;需要自建服务 |  | 原生 | 需站点开关 |
| ECharts | 声明式数据图表 | ```echarts | 围栏 | 按页加载 |
| Infographic | AntV 信息图 | ```infographic | 围栏 | 按页加载 |
| 画廊 | 一组图片共用一个缩放对话框 | ```gallery | 围栏 | 需站点开关 |
| 徽章 | 行内状态标记 | {{</* badge text="Beta" */>}} | shortcode | 无 |
| 按键 | 键位与组合键 | {{</* kbd "Ctrl" "K" */>}} | shortcode | 无 |
| 引用 | 引入文件、插入站点参数、构建期注释 | {{</* include file="parts/x.md" */>}} | shortcode | 无 |
| Asciinema | 终端录像 | {{</* asciinema file="images/x.cast" */>}} | shortcode | 按页加载 |
「运行时」列的四条细则:
- 代码块只在块上有复制或折叠按钮时加载
code-block.js;文件树只在树带注释列时加载filetree.js,它负责拖动那条分栏线。 - 图片与画廊共用一个缩放对话框运行时,需要站点开启
ui.image_zoom,且页面上确有候选图。 - 公式在构建期由 KaTeX 渲染成 HTML 与 MathML,页面上只多一份 KaTeX 样式表与字体,没有脚本。
- Draw.io 只在渲染内容含 PNG 或 SVG 候选图的页面加载,并且每个不同的图片 URL 只检查一次。
每个组件在 HTML、打印、Markdown、RSS 四种输出下都有确定形态,见各页的「输出形态」一节。
1 - 提示块
> [!NOTE] 这样的块引用写出带颜色、图标与标题的提示、警告与折叠块,不需要短代码。提示块(Callout)是 GitHub / Obsidian 风格的块引用:> [!TYPE] 起头,正文跟在后面。用于把提示、警告、前提条件从正文中分离出来;正文一句话能说清的内容不必使用提示块。
最简例子
Hugo Module 需要本机安装 Go;只用离线归档时不需要。
不写标题时使用本地化的类型名(中文站显示「注意」,英文站显示 “Note”)。源码在 GitHub 上按 GitHub 的提示块渲染,在普通 Markdown 阅读器中显示为块引用,内容都不会丢失。
十种类型
前五种与 GitHub 一致,后五种是 OINK 追加的语义类型。每种类型有默认图标与强调色。
用 hugo server -D 可以预览草稿。
主题下限是 Hugo Extended 0.160.1,低于它构建直接失败。
hugo --cleanDestinationDir 会清空 public/。
删除 resources/_gen 后第一次构建会慢很多。
构建通过、零告警——可以推上线了。
不要把 go.work 提交进仓库。
站点要不要开评论?看启用评论。
pgsty.com 就是一个只用了提示块与表格的纯文档站。
Documentation is a love letter that you write to your future self.
类型名不区分大小写。
自定义标题
标记同一行的后续文字是标题,支持行内 Markdown(代码、粗体、链接)。
public/生产构建前先确认 baseURL 指向正式域名,否则所有绝对链接都会指错。
正文内容
正文是页面级 Markdown:列表、代码围栏、表格、图片、嵌套的提示块。每一行都以 > 开头,围栏也不例外。
- 克隆:
git clone https://github.com/pgsty/oink.pgsty.com my-docs - 进入目录并预览:
- 打开 http://localhost:1313/
| 端口 | 用途 |
|---|---|
| 1313 | Hugo 开发服务器 |
折叠
类型后加 - 默认收起,加 + 默认展开;两者都渲染为原生 <details>,不加载 JavaScript。适用于完整输出、备选方案、背景说明这类不必默认展示的内容。
Hugo 通过 Go 的模块系统下载主题(hugo mod get)。用 submodule 或离线归档时可以不装 Go。
收起状态不会被记住,刷新后回到默认。
中性折叠块 DETAILS
[!DETAILS] 是没有语义颜色的折叠块:不加符号默认收起,[!DETAILS]+ 默认展开。用于冗长输出、完整配置文件等需要折叠的内容。
hugo version 输出自定义图标
块引用结束后的下一行写属性 {icon="fa-solid fa-xxx"}(一对 Font Awesome class),替换该类型的默认图标。属性行紧接块引用,中间不能有空行。
从 Pigsty v4 起默认安装 PostgreSQL 18。
嵌套
提示块可以嵌套(每层多一个 >),也可以放在列表项或步骤中。建议最多嵌套一层。
升级主题版本可能改变渲染结果。
git tag pre-upgrade 就够了——回滚只是 git checkout pre-upgrade。
未知类型与易错写法
未知的类型名不会导致构建失败,也不会丢失内容:该块渲染为普通块引用,[!TYPE] 标记原样可见。
[!NOTICE] 这不是合法类型
标记会保留在页面上提醒你。
其它常见问题:
- 正文与标题合并:经过 Prettier 等格式化工具的文件,在标题行下保留一个空的
>行,否则工具会把标题并入正文。 - 属性行被格式化工具移动:把
{icon=…}这类标记行放在<!-- prettier-ignore-start -->/<!-- prettier-ignore-end -->之间。 style、onclick等属性导致构建失败:属性行只接受icon与class(见下表)。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 静态类型是 <div class="td-callout" role="note">;折叠类型是原生 <details> + <summary> |
| 打印 | 全部静态展开,折叠块带 data-td-callout-collapsible 标记 |
| Markdown | 保留源码块引用(含 [!TYPE] 标记与标题) |
| RSS | 与打印相同,静态展开 |
提示块不加载脚本。
参数参考
标记行 > [!TYPE]± 标题:
TYPE, ,NOTETIPIMPORTANTWARNINGCAUTIONSUCCESSDANGERQUESTIONEXAMPLEQUOTEDETAILS;大小写不敏感;未知值渲染为普通块引用±, ,-折叠默认收起,+折叠默认展开;DETAILS不加符号即收起标题, ,- 与标记同一行
属性行 {…}(块引用之后紧接的一行):
icon, ,- 例如
fa-solid fa-database;DETAILS默认无图标 class, ,- 原样透传给站点 CSS
style、on* 与其它任何键都会让构建失败。
限制与常见问题
- 不能自定义颜色:颜色由类型决定,需要新语义时选最接近的类型并自定义标题。
- 折叠状态不持久化。
- 提示块可以放在
{.steps}列表项与{{%/* steps */%}}步骤中(见步骤),块引用的每一行都以>开头,缩进与列表项对齐。
相关
2 - 图片
图片只有一种写法:Markdown 的 。独立成段的图片可以在下一行跟一行 {…} 属性,成为带图注的 figure、缩放候选、编号图或经 Hugo 处理的派生图。主题没有图片 shortcode。
最简例子

这张图与本页放在同一目录(页面包)中,主题读取它的固有尺寸并写入 width/height,页面加载时不发生跳版;所有图片懒加载。替代文字供屏幕阅读器与搜索引擎使用,应当始终填写;空 alt 表示装饰性图片,缩放会跳过它。
图片来源
来源按以下顺序解析,写法相同:
| 放法 | 源码里怎么写 | 适合 |
|---|---|---|
与页面同目录(页面包 index.md + 图片) |  | 只有这一页用的截图;随页面一起移动、翻译共用 |
全局资源 assets/images/… |  | 多页共用、还要做处理(缩放 / 裁切)的图 |
静态目录 static/images/… |  | 不需要处理的大图、下载物;主题拿不到尺寸时可以用 width/height 补 |
| 远程 URL |  | 少用:构建期不会下载,也不能处理 |
相对路径先按页面资源、再按全局资源查找,都找不到时按静态路径原样输出;主题不检查静态路径与远程 URL 是否存在。只有要求处理(command=)的图找不到资源时才构建失败。
行内与块级
位于文字中间的是行内图片,渲染为一个 <img>,不能带属性;独立成段的是块级图片,可以带属性行。
这一枚小图
夹在句子里,是行内图片。

行内图片按自身尺寸显示(这里是 50×32)。没有固有尺寸的 SVG 行内插入时会被拉伸到容器宽度,SVG 应作为块级图片使用并给出 width/height。
块级图片依赖站点设置 markup.goldmark.parser.wrapStandAloneImageWithinParagraph: false(本站已配置;见配置总览)。缺少它时 Goldmark 会把独立图片包进 <p>,属性行也会被当作正文。
图注
属性行加 caption="…",图片渲染为 <figure> + <figcaption>。图注是纯文本,不解析 Markdown。

Markdown 里的 "标题" 保持原义(悬停提示),不会成为图注。
尺寸
width/height 是正整数,覆盖资源自身的尺寸:为静态或远程图片提供占位框以避免跳版,或把大图缩小显示(浏览器缩放,不改文件)。

处理型图片
页面资源与全局资源可以在构建期由 Hugo 处理:command 与 options 必须同时给出,命令是 Fit Resize Fill Crop 之一,选项是 Hugo 的图片处理字符串。渲染出的 src 是派生图;启用缩放时对话框打开原图。


静态路径、远程 URL 与 SVG 不能处理,对它们写 command 会构建失败。选项语法(锚点、质量、格式转换,如 300x150 webp q80)见 Hugo 图片处理。
链接图片
两种写法,用途不同:
- 没有图注、图片本身是链接:用 Markdown 的链接包图
[](href)。 - 有图注的 figure 整体可点:属性行加
link="…"(必须同时有caption或num)。

带链接的图不参与缩放。没有图注只写 link= 会构建失败,报错中提示改用 [](…)。
编号图
编号图用于书籍与长篇手册:属性行加 num,可选 #id。编号是作者书写的字符串(2-1、3.4),主题不自动计数;图注前加本地化的「图 2-1」前缀,#id 缺省为 fig-<num>。正文用普通链接 [图 2-1](#fig-2-1) 或 xref shortcode 引用;全书图目录见书籍出版。

见图 2-1。
编号图可以同时是处理型图片(num + command),也可以带 link。
缩放
图片缩放默认关闭。站点开启后,块级图片、figure、画廊中带 alt 的图成为可点击的按钮,在原生 <dialog> 中查看大图(Esc 关闭,焦点回到原处)。本页在 front matter 中开启了它,上面的图都可以点击。
不缩放的图:行内图、alt 为空的装饰图、带链接的图、data-no-zoom 标记的图。运行时只在页面确有候选图时加载;打印 / Markdown / RSS 中没有对话框。

深浅色图片
主题没有按深浅色切换图片的参数。需要两张图时,各写一个 class,在站点 CSS 中按 [data-bs-theme="dark"] 显示其一:
class 由主题原样透传,供站点 CSS 使用。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 行内 <img>;块级 <img class="td-image">;有图注 / 编号时 <figure class="td-figure"> + <figcaption>;缩放候选带 data-td-image-zoom |
| 打印 | 同 HTML,去掉缩放控件 |
| Markdown | 原样输出  与属性行 |
| RSS | 图片 src 改为绝对地址;无缩放 |
参数参考
属性行 {…}(块级图片之后紧接的一行):
caption, ,- 有它就渲染成 figure;不解析 Markdown
#id, ,[A-Za-z][A-Za-z0-9_.:-]*;作为锚点与 Book 目标 IDnum, ,[0-9A-Za-z.-]+;注册为 Book 图目标,图注加「图 N.」前缀width/height, ,- 覆盖尺寸;静态 / 远程图靠它避免跳版
command, ,FitResizeFillCrop;必须与options同给;仅页面 / 全局资源options, ,- Hugo 图片处理选项,如
600x300、300x150 Left、800x webp q80 link, ,- 把 figure 包进链接;需要
caption或num;带链接的图不缩放 class, ,- 透传给站点 CSS
data-*/aria-*, ,- 透传
style、on*、alt、title、src 与其它任何键出现在属性行都会构建失败(alt、title、src 属于 Markdown 图片本身)。
限制与常见问题
- 图注不含 Markdown:所有公开字符串参数都是纯文本;富文本说明写在图片下方的段落中。
title不是图注:的c是悬停提示。- 处理型图片只对资源生效:
static/中的图需要处理时移到页面包或assets/。 - 构建期不下载远程图片。
- 缩放不支持拖拽、平移、上一张 / 下一张;一组相关图片使用画廊。
相关
3 - 代码块
代码块是普通的 Markdown 围栏,高亮由 Hugo 内置的 Chroma 在构建期完成,浏览器里没有高亮器。用于命令、配置片段与源码:围栏信息行上的 {…} 属性决定标题栏、复制行为、行号与行锚点。图示类围栏(mermaid、echarts、filetree 等)不走这条路径,它们各有渲染钩子。
最简例子
没有属性的围栏同样有完整外壳与复制按钮。无标题栏时不渲染空白横条,复制按钮浮在右上角,鼠标悬停或焦点进入块内时出现,触屏设备上始终可见。外壳不显示语言名,lexer 名字只写入 data-language,供样式表与测试使用。
语言标记就是 Chroma 的 lexer 名。diff 围栏用 Chroma 的增删行样式呈现补丁,不需要额外组件:
文件名标题
title 给块加一条可见标题栏,通常写文件名或路径。它同时成为这个块的无障碍名称。
filename 是 title 的历史别名,两个一起写会构建失败。
行号、起始行与高亮
lineNos 取 inline(行号与代码同一列)或 table(行号独立成列,可单独选中不被复制)。lineNoStart 改显示的起始编号。hl_lines 标记要强调的行,计数按围栏内的源码行,从 1 开始,与 lineNoStart 无关。
lineNos="table" 把行号放进独立的一列(两种模式下复制按钮都会剔除行号):
tabWidth 决定制表符展开成几个空格,与 style 一样原样转交 Chroma。本站使用基于 class 的 Chroma 调色板(深浅色各一套),style 只在把 Hugo 切回内联样式模式时才生效。
长行换行
wrap=true 只改变显示:源码不变,复制出来的文本也不变。不加它时长行横向滚动。
wrap=true 与表格行号不能共存:行号列与代码列是两个表格单元格,换行后会错位。写在一起构建失败,报错提示改用 lineNos="inline" 或去掉换行。
折叠长代码
collapse=N 让块初始只显示 N 行,底部给一个「显示全部 N 行」按钮。服务器输出完整代码,折叠是浏览器量出第 N 行位置后的视觉裁切:没有 JavaScript 时、读屏器中、打印时代码都是完整的。
行数不超过 collapse 时按钮不出现。换行与折叠可以一起用:折叠测量的是第 N 个源码行节点的底边,换行的行不会被截断。
复制内容
默认复制整块源码。终端会话(console 与 shell-session 两个 lexer)默认只复制命令:带提示符的行留下,提示符本身与输出行去掉。下面这个块复制出来只有两条命令,没有 $ 也没有输出。
要连提示符与输出一起复制就写 copy="all"。把 copy="command" 用在 bash、sh 之类普通 lexer 上会构建失败,因为它们分不出提示符、命令与输出。多行命令请在续行里写出续行提示符(通常是 >),否则那一行会被当成输出而排除。
会话 lexer 的块里一行提示符都没有时,复制按钮报失败:图标转为错误状态,控制台留一条错误,剪贴板不变。它不会退化成复制全文。
copy=false 关掉这一块的复制按钮,用于不应被抄走的反例片段:
整站关掉复制用 params.ui.code_copy: false,它优先于每个块自己写的 copy(见配置总览)。复制按钮只有图标,成功与失败会换图标并播报本地化状态;复制内容保留缩进、空行与 Unicode,去掉行号,末尾只留一个换行。
行链接与稳定 ID
把「看第 3 行」做成链接需要两步:给围栏一个明确的 id,再打开 anchorLineNos=true。行号随即变成锚点链接,锚点是 #<id>-<行号>。
跳到 第 4 行。
不写 id 时主题也会生成一个页面内唯一的 ID,但它依赖围栏在页面里的顺序,前面插入一个新围栏就会变。只有作者书写的 id 才是永久链接。ID 不能含空白与控制字符,也不能与页面上其它块的 viewport、标签、面板、标题、行锚点 ID 重复,重复即构建失败。
编号例
写书或长手册时给代码片段编号:num 加 caption,这个围栏就成了一条 Book「示例」目标,可以被 xref 引用,也会进入全书的示例目录。编号由作者书写,主题不自动计数;id 默认是 eg-<num>。
参见 示例 4-1。
num 与 caption 必须成对出现,只写一个会构建失败;num 与标签页属性 tab 互斥。图、表、公式的编号写法与索引见书籍出版。
一组围栏做成标签页
连续几个带 tab 的围栏会在浏览器里合成一个标签页集,第一个围栏上的 group 让它可分享、可同步、可记住选择。
完整规则(分组语法、URL hash、跨组同步、正文标签页)在标签页。
易错写法
- 在文档里展示 shortcode:围栏不阻止 Hugo 解析,写在代码块里的
{{< tabs >}}仍会执行。要让它原样显示,在两侧定界符的内侧各加一对注释符号,写成{{</* tabs */>}},百分号形式对应{{%/* steps */%}}。本页每一处展示 shortcode 的地方都是这么写的。 - 围栏里套围栏:外层用四个反引号、内层三个,本页每一段「源码」都是这么写的;内层还有围栏时外层再加一个。
- 属性写在信息行上:围栏的属性跟在开栏那一行的语言后面,表格与图片的属性才写在块的下一行。写到下一行会变成正文里一段可见的花括号。
- 未知属性会失败,不会被忽略,错误信息里列出允许的名字。
style、srcdoc与on*被拒绝;data-td-code*前缀以及data-language、data-line-count、data-collapse-lines是主题的保留名,写上去同样构建失败。 - 列表项里的围栏:缩进要与列表项内容对齐(
1.之后恒定三个空格),否则围栏会脱离列表。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-code"> 外壳 + Chroma 的 .highlight/.chroma;复制、折叠按钮在服务器输出里是 hidden,脚本确认可用后才显示 |
| 打印 | 完整代码,去掉复制、折叠、渐隐;长块允许跨页;标题栏保留 |
| Markdown | 原样输出源码围栏,连 {…} 属性一起 |
| RSS | 静态代码块,无按钮 |
没有复制或折叠控件的页面不加载 code-block.js;打印、Markdown 与 RSS 输出不加载。
参数参考
开栏那一行、语言之后的 {…} 里,OINK 自己的属性:
title, ,- 可见标题栏(通常是文件名),同时是无障碍名称
filename, ,title的历史别名;两者同时出现构建失败copy, ,true等价于all;command只允许console/shell-sessionwrap, ,- 视觉换行,不改源码;与表格行号互斥
collapse, ,- 初始显示的最大行数;行数不足时不生效
label, ,- 无障碍名称,不显示在页面上;与
aria-label互斥 id, ,- 稳定的块 ID 与行锚点前缀;不能含空白
tab, ,- 标签名,见标签页;与
num互斥 group, ,- 写在一组的第一个围栏上,启用 hash / 同步 / 持久化;需要
tab value, ,- 分组内每个围栏必填,无分组时禁止;需要
tab num, ,- 编号示例(Book
eg);必须与caption同时出现 caption, ,- 编号示例的说明;必须与
num同时出现 class, ,- 追加到
.td-code根元素 data-*/aria-*/role, ,- 透传到根元素
title、filename 与 label 已经为块生成了无障碍名称与 role="group"。它们中的任意一个与 aria-label、aria-labelledby 或 role 同时出现都会构建失败;这三个属性只在块没有标题也没有 label 时可以透传。
同一行还能写 Chroma 选项,主题原样转交 Hugo:
lineNos, ,- 行号形态;
table与wrap=true互斥 lineNoStart, ,- 显示的起始行号,不影响
hl_lines的计数 hl_lines, ,- 如
"2 4-5",按围栏内源码行计数 anchorLineNos, ,- 行号变成锚点链接,前缀取自块的
id tabWidth, ,- 制表符展开的空格数
限制与常见问题
- 不换高亮器:没有 Shiki、Twoslash、浏览器端高亮,也没有可执行的代码演练场。补丁用
diff围栏,Chroma 的.gi/.gd就是增删行的样式。 copy="command"只认会话 lexer:写在别的语言上是构建错误,不会退化成复制全部。- 自动生成的 ID 不是永久链接:要发链接就写
id。 mermaid、math、chem、markmap、plantuml、echarts、infographic、checksums、filetree、gallery不是代码块:它们有各自的渲染钩子,不套这层外壳,也没有复制按钮。
相关
4 - 标签页
{tab=} 属性就得到标签页;加上 group 之后可分享链接、跨组同步、记住读者的选择。标签页并列等价的几种写法:包管理器、发行版、YAML / TOML / JSON、环境变量与配置项。有先后的步骤、互不相关的内容不适合标签页,读者一次只看见其中一个。
原生形态是给相邻的块加 tab 属性。正文(多个段落、列表、提示块)要做成标签页时才用 tabs/tab shortcode。两种形态共用一个运行时、一套 DOM 与一样的键盘行为。
最简例子
连着写两个带 tab 的围栏,中间只隔空行。
服务器输出两个带标题的代码块,没有面板被隐藏;页面加载后运行时把相邻的同类块重组为标签页。在 GitHub 上、打印时、关闭 JavaScript 时,读者看到的是连续两块完整内容。
分组:链接、同步与记忆
只在第一个块上写 group,这一组就有了公开的 URL hash #<group>-<value>、页内同步与浏览器持久化;分组内的每个块都要写 value。
value 是机器值(^[a-z0-9][a-z0-9_-]*$),tab 是给人看的标签名,两者互不相干。上面这组的 pnpm 面板对应的 hash 是 #pkgmgr-pnpm,带这个 hash 访问本页会直接选中它。
同组联动
下面这组用了同一个 group="pkgmgr"。在上面那组切换包管理器,这组会跟着切;在这组切换,上面那组也跟着切。选择写入 localStorage 的 td-tabs:v1:pkgmgr 键,在其它页面同组的标签页上仍然生效。
这组没有 yarn 面板。同步时缺哪个值就保持不动,不会出现「一组没有选中项」的状态。初始选哪个的优先级是:URL hash,存储的值,shortcode 的 default 或第一个块,第一个标签。带 hash 打开页面只切换,不覆盖读者已经存下的偏好。
表格也能做标签页
同一套属性写在表格的属性行上,连着的表格就组成一组标签页。
| 参数 | 默认值 |
|---|---|
shared_buffers | 25% RAM |
max_connections | 100 |
| 参数 | 默认值 |
|---|---|
shared_buffers | 128MB |
max_connections | 100 |
围栏与表格是两种块类型,相邻也不会合成同一组:一组标签页里只能全是围栏或全是表格。两者混排使用下面的 shortcode 形态。
标签名与文件名共存
围栏的 tab 和 title 可以一起写:标签名进标签栏,文件名标题栏留在面板里。
单独一个块只是带标题的块
一个块要凑够两个相邻的同类块才会变成标签页。落单的块保留标题,不会变成只有一个标签的标签栏。
块之间只允许空行。三种情况会断开一组:中间隔了正文(段落、标题、列表都算);中间有一条 HTML 注释,<!-- prettier-ignore-end --> 是常见的一处;后一个块自己写了 group,一组里只有第一个块可以带 group。
正文标签页
面板里要放段落、列表、提示块或多个块时,用 tabs/tab shortcode。正文是完整的 Markdown。
仓库自带 .github/workflows/,推到 main 就会构建并发布。
baseURL 要写成仓库的 Pages 地址。
在 Cloudflare 控制台里连接仓库,构建命令:
default 指定初始选中的面板,它必须是某个子项的 value,并且需要 group。没有 group 时不能写 value,主题自动生成 tab1、tab2 等值,这组标签页只在本地切换,不动 URL 也不写存储。shortcode 形态比属性形态严格:写错的地方在构建期就报出来,不留到浏览器里。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-tabs"> + role="tablist" 的按钮与面板;运行时接管前所有面板都可见 |
| 打印 | 连续的带标题静态分节,没有标签栏 |
| Markdown | 围栏形态保持源码围栏(含 {tab=} 属性);shortcode 形态输出 **标签名** 加正文 |
| RSS | 与打印相同,堆叠的带标题分节 |
只有用到标签页的页面才加载 tabs.js;打印、Markdown 与 RSS 输出不加载。
参数参考
写在围栏信息行或表格属性行上的属性:
tab, ,- 可见标签名;单独出现时就是这个块的标题
group, ,- 写在一组的第一个块上,启用 hash、页内同步与持久化;需要
tab value, ,- 分组内每个块必填,无分组时禁止;需要
tab
tabs shortcode:
group, ,- 同上,启用 hash、同步与持久化
default, ,- 初始选中的面板;需要
group label, ,- 标签栏的无障碍名称,不显示在页面上
tab shortcode:
label, , required- 可见标签名
value, , required- 无分组时禁止书写,自动生成
tab1、tab2等值
行为约定:面板 ID 在分组里是 <group>-<value>,同一页出现第二组同名 group 时后续各组的 ID 加 -2、-3 后缀(深链目标始终是第一组),未分组时由主题生成;存储键是 td-tabs:v1:<group>;用户点击或按键会用 replaceState 更新 hash 并写入存储,带 hash 访问只切换不写入。键盘上左右方向键(感知 RTL)与 Home/End 移动并激活标签,焦点停留在标签上。
限制与常见问题
- 构建失败的写法:属性形态里
value缺group、group或value缺tab、tab与编号属性num同时出现;shortcode 形态里一组内value重复、tabs没有tab子项、子项之间夹着正文、default不匹配任何子项的value。 - 属性形态的分组错误不中断构建,只在浏览器控制台留警告:分组内漏写
value时整组丢掉group,退化成只在本地切换的标签页,hash、同步与持久化都没有;value重复时整组跳过,那几个块保持为各自带标题的块。 - 围栏与表格不会混成一组,正文与代码混排请用 shortcode 形态。
- 标签页不是折叠块。只想收起长输出用
> [!DETAILS](见提示块)。 - 同名
group是全站共享的:读者在 A 页选了 pnpm,B 页同组的标签页也会是 pnpm。这是它的用途,也意味着group名要按含义取,不用tabs1这种。
相关
5 - 表格
表格是普通的 GFM 管道表格。主题的表格渲染钩子把每张表包进一块可横向滚动的区域,表格下面那一行 {…} 属性决定它是哪一种表:带标题的表、兼容矩阵、参数表、编号表或标签页。合并单元格、排序与筛选不在能力范围内,需要它们的场景请改换呈现方式。
最简例子
不写属性行就是一张普通表。对齐方式照旧来自分隔行,表头单元格是 th scope="col"。
| 组件 | 端口 | 用途 |
|---|---|---|
| PostgreSQL | 5432 | 数据库 |
| Pgbouncer | 6432 | 连接池 |
| Patroni | 8008 | 高可用编排 |
宽表格自己滚动
列太多的表不会把页面撑宽,它在自己的区域里横向滚动。这块区域可以用键盘聚焦:Tab 停入后方向键滚动,无障碍名称是本地化的「可横向滚动的表格」。
| 集群 | 角色 | 版本 | 状态 | 延迟 | 连接数 | 大小 | 备份 |
|---|---|---|---|---|---|---|---|
| pg-meta | primary | 18.1 | running | — | 42 | 12 GB | 2026-08-17 |
| pg-test | replica | 18.1 | streaming | 12 ms | 8 | 12 GB | 2026-08-17 |
表格标题
{caption="…"} 加一个可见的 <caption>,纯文本,不给表编号。
| 条目 | 取值 |
|---|---|
| 主题版本 | v0.6.0 |
| Hugo 下限 | 0.160.1 Extended |
| 许可证 | Apache-2.0 |
兼容矩阵
{.matrix} 用于「行 × 列 = 支持与否」的对照表:第一列成为行表头(th scope="row"),滚动时表头行与第一列吸附不动,其余单元格居中,分隔行另有对齐时以分隔行为准。✅ 与 ❌ 是作者写的字符,主题不解析它们。
| OS / PG | PG18 | PG17 | PG16 | PG15 | PG14 |
|---|---|---|---|---|---|
| EL 9 | ✅ | ✅ | ✅ | ✅ | ✅ |
| EL 8 | ✅ | ✅ | ✅ | ✅ | ✅ |
| Debian 13 | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ubuntu 24.04 | ✅ | ✅ | ✅ | ✅ | ❌ |
用整个画布
{.full-width} 让表格越出正文栏宽,占满文章可用的宽度。适合列多但每列都短的表。
| 语言 | 代码 | 侧栏 | 搜索 | 目录 | 打印 | 状态 |
|---|---|---|---|---|---|---|
| 简体中文 | zh | ✅ | ✅ | ✅ | ✅ | 已审校 |
| English | en | ✅ | ✅ | ✅ | ✅ | 已审校 |
参数表
{.fields} 把表格变成定义列表:第一列是名称,最后一列是说明,中间列是元数据。它是记录配置项、命令参数、API 字段的形态,写法见参数表。
offline_search, ,- 构建本地搜索索引
page_width, ,- 正文栏宽度
编号表
写书或长手册时给表编号:num 加可选的 #id 与 caption。表格会被包进一个带本地化「表 N.」标签的 <figure>,并注册成 Book 目标,可以被 xref 引用、进入全书表格目录。编号由作者书写,主题不自动计数;id 缺省是 tbl-<num>。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读已提交 | 否 | 是 | 是 |
| 可重复读 | 否 | 否 | 是 |
| 可串行化 | 否 | 否 | 否 |
参见 表 9-1。
表格做成标签页
连着的表格加 {tab="…"} 就组成一组标签页,规则与相邻围栏一致:第一张表上的 group 启用 hash、同步与持久化,此后每张表都要 value。完整规则见标签页。
| 目录 | 内容 |
|---|---|
content/ | 页面 |
data/ | 首页与发布数据 |
| 目录 | 内容 |
|---|---|
assets/ | SCSS 与图片资源 |
static/ | 原样拷贝的文件 |
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-table-scroll"> 可聚焦滚动区 + <table>;矩阵与全宽是这个包装器上的修饰 class |
| 打印 | 完整表格按页宽排版;包装器仍在,但标成 td-table-scroll--static,不再是可聚焦视口 |
| Markdown | 原样输出源码表格与属性行 |
| RSS | 完整静态表格 |
表格不加载任何脚本。
参数参考
表格下一行的属性行:
.full-width, ,- 越出正文栏宽,占满文章画布
.matrix, ,- 第一列作行表头,表头与首列吸附,其余单元格居中
.fields, ,- 渲染成定义列表,见参数表
caption, ,- 可见表格标题;在
.fields上是列表的标签 meta, ,- 命名
.fields中间列的语义,取值typerequireddefault-;必须与.fields同用 #id, ,[A-Za-z][A-Za-z0-9_.:-]*;写在<table>(编号表则写在<figure>)上num, ,[0-9A-Za-z.-]+;注册为 Book 表目标,标题前加「表 N.」tab/group/value, ,- 相邻表格组成标签页
class, ,- 站点 CSS 用,原样留在
<table>上 data-*/aria-*, ,- 透传
style、on* 与任何其它键都会让构建失败。
限制与常见问题
- 互斥规则:
.fields不能和.matrix、.full-width或num一起用;num与tab互斥;group/value需要tab;meta需要.fields。 - 属性行必须紧贴表格:中间空一行,它就变成正文里一段可见的花括号。Markdown 格式化工具常移动这一行,把它包进
<!-- prettier-ignore-start -->/<!-- prettier-ignore-end -->。 - 没有合并单元格、没有排序、没有筛选:GFM 管道表格能表达的就是全部。需要合并表头的复杂表请拆成两张表或改成一张矩阵。
- 单元格里放不下块内容:多段说明、列表、围栏要用
fields/fieldshortcode。 .matrix的居中由 CSS 实现:分隔行里写了对齐就以分隔行为准。
相关
6 - 参数表
{.fields} 记录配置项、命令参数与 API 字段:名称、类型、默认值、说明各就各位,窄屏不挤,每条都能单独链接。参数表(Fields)把「一串具名值 + 元数据 + 说明」渲染成响应式定义列表:名称独占一行,类型、是否必填、默认值是名称旁边的小字,说明另起一行,每一条自带锚点。用于配置项、命令参数与 API 字段。要按同一批列横向比较很多行时用普通表格,内容是操作顺序时用步骤。
写法有两种:普通表格加 {.fields}(默认选它),以及 fields/field shortcode(说明需要多个段落、列表或代码块时才用)。两种形态渲染出相同的条目。
最简例子
一张至少两列的管道表格,下一行写 {.fields}。第一列是名称,最后一列是说明,中间每一列都是元数据,标签就是表头文字本身。
offline_search, ,- 构建本地搜索索引并启用命令面板
offline_search_max_results, ,- 搜索结果条数上限
page_width, ,- 正文栏宽度,可选
narrownormalwide
这里的元数据显示成「表头: 值」。主题不推断表头的含义,类型 只是一个标签;要让它变成标准芯片见下一节。单元格接受行内 Markdown(代码、强调、链接),空的中间单元格省略。
语义列 meta=
meta 按顺序说明每一个中间列扮演什么角色:type(类型)、required(必填)、default(默认值),或者 -(保留表头当标签)。有了它,表格形态渲染出的芯片与 shortcode 形态一致。
baseURL, , required- 站点地址,含子路径
title, , required- 站点名,出现在顶栏与页签
defaultContentLanguage, ,- 默认语言,决定无前缀路径属于哪种语言
规则:
meta必须为每一个中间列写一个角色,个数等于总列数减二;写多写少都构建失败。required列是「非空即真」:单元格里写「是」「yes」「✔」都一样,渲染出来的是不翻译的required芯片;留空就不显示。type与default单元格如果本身没有行内标记,会自动套上代码格式,与 shortcode 形态对齐。- 三种语义芯片按
type、required、default的顺序显示,与列的顺序无关;-列跟在后面,按列顺序排。
- 可以和语义角色混用,用来保留一列自定义标签:
HUGO_MODULE_WORKSPACE, ,- 指向
go.work,让主题从本地 checkout 解析 HUGO_ENV, ,- 设为
production时启用压缩与指纹
标签与容器 ID
caption 给整张表加一个可见标签(同时是无障碍名称),id 命名外层容器,方便从别处链接过来或写站点 CSS。
params.ui.image_zoom
enable, ,- 打开图片缩放
selector, ,- 扫描候选图片的根选择器
每一条都能单独链接
每个条目获得一个 field-<名称> 形式的锚点,鼠标移上去时名称右边出现自链接图标。上面第一张表里的 page_width 就是 #field-page_width,回答问题时可以把这一行的链接单独发出去。
同一页里重名的字段按 -2、-3 顺延,规则与 Goldmark 处理重名标题一致。锚点只在 HTML 里生成:打印和 RSS 会把很多页拼成一个文档,页内锚点在那里会冲突。
shortcode 形态
说明需要多个段落、列表或代码块时,表格单元格装不下,改用 fields/field:
pig 命令常用参数
--config, , required配置文件路径。相对路径按当前工作目录解析。
如果同时设置了
PIG_CONFIG环境变量,命令行参数优先。--log-level, ,日志级别,从低到高:
debug:打印每一次远程调用info:默认值error:只在失败时输出
--dry-run, ,只打印将要执行的动作,不改任何东西:
required=true 与 default=false 是布尔值,不加引号。default 接受任何标量:default=0、default="" 都会如实显示(空字符串显示成 ""),不写 default 就不显示这一项。每个 field 必须有非空正文,并且必须是 fields 的直接子项。
两种形态的选择
| 情况 | 用法 |
|---|---|
| 每条说明一句话,能放进表格单元格 | 表格 + {.fields} |
| 说明要分段、带列表或代码块 | fields/field shortcode |
| 读者需要按同一批列横向比较很多行 | 用普通表格,不转成参数表 |
| 内容是操作顺序 | 用步骤 |
表格形态在 GitHub 上仍然是一张可读的表,OINK 的 Markdown 输出也保持表格原样,这是默认选它的理由。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-fields"> + 语义 <dl>;条目带 #field-<名称> 锚点与自链接 |
| 打印 | 完整定义列表,不带条目锚点 |
| Markdown | 表格形态保留源码表格;shortcode 形态输出「名称 — 类型;required;default: 值」加缩进说明的项目符号列表 |
| RSS | 完整静态 <dl>,不带条目锚点 |
不加载任何脚本。
参数参考
表格属性行(写在表格下一行):
.fields, ,- 必需;把表格渲染成参数表
meta, ,- 空格分隔,取值
typerequireddefault-;个数等于中间列数;语义角色不可重复 caption, ,- 可见标签,同时是列表的无障碍名称
id, ,- 外层容器的 ID
class, ,- 透传给站点 CSS
data-*/aria-*, ,- 透传
fields shortcode:
label, , required- 可见标签,作用同表格的
caption id, , required- 外层容器 ID;不能含空白、引号、
<、>、& class/data-*/aria-*, , required- 与表格属性行同一套策略
field shortcode:
name, , required- 字段名
type, , required- 类型标签,如
booleanstring[]duration required, , requiredtrue时显示不翻译的required芯片,默认falsedefault, , required- 字符串 / 布尔 / 整数 / 浮点;
false、0、""都会显示
限制与常见问题
- 第一列必须非空,且在同一张表内唯一:重名或空名构建失败。
.fields不能与.matrix、.full-width、num组合,meta不能用在没有.fields的表上。- 表格单元格里放不下块内容:需要段落、列表、围栏就换 shortcode 形态。
required与default是不翻译的 API 词汇,在所有语言下都显示英文,它们是契约词,不是界面文案。- 暂不支持
kind、since、deprecated、location、字段级链接与嵌套结构,也不会在构建时解析 TypeScript 或 OpenAPI schema。
相关
7 - 步骤
{.steps} 就是带编号圆点与竖线的操作步骤;步骤要带标题、要进目录时改用 steps shortcode。步骤(Steps)是带编号圆点与竖线的有序列表:一个普通有序列表,加一行 {.steps} 标记,编号圆点与串起它们的竖线由 CSS 绘制,不加载脚本。用于有先后的操作流程。并列而无先后的内容用普通列表或卡片。
写法有两种:有序列表加 {.steps}(默认选它),以及 {{% steps %}} shortcode,每一步要有自己的标题、标题还要进右侧目录时用它。
最简例子
每一项都写 1.,让 Markdown 自己数。这样插入、删除、调换步骤都不用手改编号,而且内容缩进恒定是三个空格。
- 安装 Hugo Extended
- 克隆文档站
- 启动本地预览
{.steps} 必须紧贴列表最后一行,中间空一行它就会变成正文里一段可见的花括号。
步骤内容
列表项里可以放任何块级内容:段落、代码围栏、提示块、表格、嵌套列表、图片。缩进对齐到列表项的内容列(三个空格)即可。
克隆文档站,它本身就是主题的完整示例。
启动本地服务器。
说明首次构建会通过 Go 模块代理拉取主题,需要本机安装 Go。
替换三处内容,它就是你的站点。
位置 替换为 hugo.yml的title你的站名 hugo.yml的baseURL你的域名 content/你的内容
{{< … >}} 形式的 shortcode(标签页、卡片、徽章等)也可以写在列表项里;{{% … %}} 形式不行,见下面的限制。
一步里按平台分开
某一步在不同平台上命令不同时,把带 {tab=} 的围栏并排写进那个列表项,它们照样会合成标签页。
安装 Hugo Extended。
安装依赖:
EL / RHELDebian / Ubuntu运行
hugo server预览。
接着上一组往下编号
正文隔断了一组步骤时,把新一组的第一项写成它实际的序号,Markdown 会输出 start,编号从那里继续(支持到 40)。
- 配置
baseURL与部署工作流。 - 推送到
main,等待 GitHub Actions 构建完成。
带标题的步骤
步骤本身很长、每一步该有个能被链接和被目录收录的标题时,用 {{% steps %}}:它的正文是页面级 Markdown,里面的每一个直接子标题就是一步,正文不用缩进。下面三步的标题就在这一页的右侧目录里。
安装工具链
需要 Hugo Extended ≥ 0.160.1 与 Go。
启动服务器
brew install hugo go
sudo apt install hugo golang-go
发布
推送到 main,仓库自带的工作流会构建并发布。
它是主题里唯一的 {{% … %}} shortcode。百分号形式的正文交给 Goldmark 当页面级 Markdown 处理:只有这样,里面的标题才能进目录,里面才能放 tabs、cards、fields 这些容器 shortcode。代价是它自己不能嵌进列表项,也不能嵌进另一个百分号容器。
同一组步骤的标题保持同一层级,不要把一个 steps 套进另一个里。
两种形态的选择
| 情况 | 用法 |
|---|---|
| 步骤是一两句话加一段命令 | 有序列表 + {.steps} |
| 每一步需要标题、需要被链接、需要进目录 | {{% steps %}} |
步骤里要放 tabs、cards、fields 容器 | {{% steps %}} |
| 步骤本身要嵌在另一个列表项里 | 有序列表 + {.steps} |
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 原生形态是 <ol class="steps">,编号与竖线由 CSS 画;shortcode 形态是 <div class="td-steps"> 加各级标题 |
| 打印 | 编号与内容照旧,竖线保留 |
| Markdown | 原样输出源码:有序列表加 {.steps},或标题加正文 |
| RSS | 静态列表 / 标题分节 |
不加载脚本;关闭 JavaScript 后呈现不变。
参数参考
两种形态都没有参数,只有写法约定:
{.steps},- 必需;写在无序列表上不生效
1.,- 让 Markdown 自己数;内容缩进恒为三个空格
,4.(首项)- 输出
<ol start="4">,编号从 4 接着走,支持 2–40 {{% steps %}},- 直接子标题(
##–######)就是步骤;正文不缩进
限制与常见问题
- 列表项里不能写
{{% … %}}:百分号 shortcode 的多行输出会把列表截断。要在步骤里放容器就整组改用 shortcode 形态。 {{% steps %}}不能放进列表项,也不能套在另一个百分号容器里。- 标记要紧贴列表:
{.steps}与列表之间不能有空行;经过 Prettier 之类的格式化工具时,把它包进<!-- prettier-ignore-start -->/<!-- prettier-ignore-end -->。 {.steps}只对有序列表有效:写在-开头的无序列表上不会有编号。- 步骤不折叠、不记进度:没有「已完成」状态,也没有展开收起。
相关
8 - 卡片
{.cards} 的链接列表排出导航卡片网格;需要图标、徽章、图片时改用 shortcode。卡片(Cards)是一组并列的链接:每张卡片一个链接标题加一句描述,网格随容器宽度自适应。适合栏目首页、「接下来读什么」与几条并列路径的入口。不适合排版正文段落(用普通段落)或做图片墙(用画廊)。
最简例子
带 {.cards} 的链接列表就是卡片。链接是标题,— 之后是描述。
整张卡片是点击热区,不只是标题文字。没有 columns 参数:列数由容器宽度决定,窄屏收成一列。
只有标题的卡片
描述可以省略。一行一个链接,{.cards} 收尾。
松散列表与多段描述
一句话装不下时改用松散列表:链接单独一段,描述另起一段,列表项之间空一行。标题独占一行,描述在标题下方。{.cards} 仍然紧贴最后一段,中间 不能有空行。
图标与徽章
链接列表不支持图标、徽章、图片与多段描述,这些用 cards / card shortcode。icon 是恰好一对 Font Awesome class,badge 是一段纯文本。
图标格式不符(不是 fa-solid fa-xxx 这样的一对 class)时构建失败,不会静默丢弃。
Markdown 正文
card 的正文按页面级 Markdown 渲染:行内代码、强调、链接、列表都可以。title、badge 这些参数是纯文本,不解析 Markdown。
hugo mod get github.com/pgsty/oink。推荐方式,升级只需改一行版本号。
无需安装 Go:
git submodule add- 主题落在
themes/oink
不写 link 的卡片渲染成加粗标题,不生成链接。
带图片的卡片
image 与  的解析顺序一致:页面资源 → 全局资源 assets/ → 静态路径 /images/… → 远程 URL。本地资源带上固有尺寸,避免加载跳版。
image 必须配一个替代文字来源:image_alt="…"(有信息的图)或 decorative=true(纯装饰)。两个都写、两个都不写都会构建失败。
卡片图片不参与图片缩放,整张卡片本身已经是链接。
栏目首页的自动卡片
栏目首页(_index.md)不需要手写卡片列表:主题读子页的 title、description、icon 自动生成一组卡片。本站在 hugo.yml 中全局启用:
单个栏目可以在自己的 front matter 里覆盖,也可以用 cascade 把选择推给整棵子树:
自动卡片与手写卡片使用同一套 td-content-card 样式,区别只在数据来源。栏目首页不要手写子页清单:手写清单会与侧栏不同步。要排的内容不是本栏目的子页时(例如混合站外链接、跨栏目推荐),才在正文里手写卡片。相关键的完整定义见配置总览。
两种形态的选择
| 你要的 | 用哪种 |
|---|---|
| 一句话描述的链接网格 | {.cards} 链接列表 |
| 图标、徽章、图片 | cards / card shortcode |
| 描述里要列表、代码、多段 | cards / card shortcode |
| 没有链接的卡片 | cards / card shortcode |
| 本栏目的子页 | 什么都不写,靠 section_index: cards |
链接列表在 GitHub 上仍是一个链接列表,shortcode 不是。能用原生形态时用原生形态。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 原生形态是 <ul class="cards">;shortcode 形态是 <div class="td-content-cards"> + 每张 <article class="td-content-card">。两者都是纯 CSS 网格,不加载脚本 |
| 打印 | 原生形态竖排,shortcode 形态收成两列;两者的单张卡片都避免跨页断开 |
| Markdown | 原生形态原样输出链接列表;shortcode 形态输出 - [标题](链接) (徽章) — 描述 |
| RSS | 与 HTML 同样的标记(没有站点 CSS 时是一份可读的链接清单) |
参数参考
原生形态:
{.cards}, ,- 写在无序列表 之后 的一行;只对无序列表生效
列表项首个链接, ,- 卡片标题,同时是整张卡片的点击目标
其余内容, ,- 描述。紧凑列表里跟在
—后面,松散列表里另起一段
card 的参数(cards 自身不接受任何参数):
title, ,- 必填,非空。卡片标题
link, ,- 站内路径、相对路径、
http(s):、mailto:;外链自动加rel="noopener" icon, ,- 例如
fa-solid fa-rocket;格式不符构建失败 badge, ,- 标题右侧的小标签
image, ,- 页面资源 / 全局资源 / 静态路径 / 远程 URL
image_alt, ,- 有
image时与decorative二选一 decorative, ,true表示装饰图,输出空 alt正文, ,- 卡片描述
没有 cols、columns、accent、desc、color 参数;未知参数一律构建失败。
限制与常见问题
{.cards}只认无序列表:有序列表加了这个标记不会变成卡片。{.cards}必须紧贴列表:中间空一行、或缩进进列表项,标记被静默丢弃,构建不报错,列表仍是列表。渲染结果不是卡片时先检查这一行。card只能待在cards里:单独使用、或放进别的 shortcode,构建失败并指出位置。- 列数不可配:网格按容器宽度自适应,只有栏目首页的自动卡片能用
params.ui.section_index_columns指定列数。 - 卡片不放长文:描述超过两行时改用正文段落或提示块。
相关
9 - 文件树
filetree 围栏画带注释的目录结构:对齐的注释列、逐条目图标、可折叠目录、可拖动的分栏。文件树(FileTree)是一个 filetree 围栏,围栏正文就是目录清单:缩进表示层级,结尾的 / 表示目录,# 之后是注释。适合解释一份目录结构里与读者有关的那部分,并逐条加上说明。需要读者逐字复制的清单用普通代码块。
最简例子
- content/
- _index.zh.md
- docs/
- blog/
- hugo.yml
- go.mod
项目符号(-、*、+)可以省略,效果相同。有子项的条目是目录;没有子项时,结尾的 / 告诉主题它是目录。
加注释
每行第一个前面带空白的 # 之后是注释,渲染成对齐的右列。注释是纯文本,里面的 Markdown 按字面显示;要一个字面井号就写 \#。
- content/全部页面,中英双语同目录
- docs/你正在读的这棵文档树
- blog/发布说明与文章
- assets/scss/站点自己的 SCSS,覆盖主题变量
- layouts/站点级模板覆盖,越少越好
- static/images/不需要构建期处理的图
- hugo.yml站点配置:语言、菜单、params.ui
注释列的起点在构建期算出,由最宽的一行决定,因此每行的 # 从同一列开始,与源码里是否对齐无关。注释列最多占面板的右半边,最少占三成。中间的虚线是分隔条,可以拖动,也可以用 Tab 聚焦后按方向键调整(Home / End 到两端)。
过长的名称与注释各自在本列内用省略号截断,鼠标悬停时由 title 提示完整文本。分隔条是文件树唯一的 JavaScript,只有 带注释 的树才加载它。
两列都发生截断
- runbooks/
- a-deliberately-long-runbook-filename-for-a-failover-drill.md同样超长的注释,写在一行里,因此必须在注释列内截断
- restart.md短名字
标题栏
围栏属性 {title="…"} 在树上方渲染一条标题栏;不写时没有标题栏。
oink.pgsty.com 仓库根目录
- content/页面
- assets/参与构建的资源
- data/首页、Landing、下载页的数据
- layouts/模板覆盖
- static/原样拷贝的文件
- tests/Playwright 与 node --test
- hugo.yml
- go.mod用 Hugo Module 引入主题
- Makefilemake d / make b / make c
缩进与层级
层级由缩进决定。两个空格、四个空格、制表符(按四列计算)都可以,同一棵树内不要求统一,条件是每次退回的层级此前已经打开过。tree 命令的输出可以整段粘贴,包括开头的根目录行与结尾的统计行,统计行会被丢弃。
- content/docs
- about
- _index.zh.md
- features.zh.md
- components
- filetree.zh.md
- image
- index.zh.md
- _index.zh.md
- about
退回到未打开过的缩进层级时构建失败,报错里带围栏内的行号。
折叠与显式类型
有子项的目录默认展开,{open=false} 使其初始收起。目录用原生 <details> 渲染,键盘可操作,不需要 JavaScript。open 只能写在目录上。没有子项、名字也不以 / 结尾的条目按文件处理,{type=dir} 覆盖这个判断,{type=file} 同理。
内容目录
- content/
- docs/新文档树
- components/22 个组件页
- callout.zh.md
- filetree.zh.md
- image/页面包:正文 + 图
- customize/站点级配置
- config.zh.md
- components/22 个组件页
- blog/
- release.zh.md
- docs/新文档树
图标与配色
图标默认按名字推断:目录用文件夹图标,随开合切换;文件先按完整文件名匹配(LICENSE、Makefile、go.mod、package.json、.gitignore 等),再按扩展名匹配(md yml toml json sh py go js sql css png svg pdf zip 等),都不匹配时用普通文件图标。
{icon=…} 覆盖它,取值是恰好一对 Font Awesome class。{tone=…} 给图标上色,取值与徽章相同:neutral info success warning danger。
部署目录:权限与要点
- /etc/pigsty/0755 root:root · 配置根目录
- pigsty.yml0644 root:root · 集群清单
- ca/0700 root:root · 自签 CA,不要提交进 Git
- ca.key0600 root:root
- /var/lib/pgsql/18/data/0700 postgres:postgres · 数据目录
- postgresql.conf0600 postgres:postgres
- /usr/bin/pig0755 root:root · 命令行工具
tone 只给图标上色,不改文字。颜色是补充,含义写在名字或注释里。
条目链接
条目名写成 [名字](链接) 即为链接。站内路径、相对路径、http(s): 都可以,URL 校验与其它组件是同一套。
本站的组件页
- content/docs/components/
- callout.zh.md提示块
- filetree.zh.md当前页面
- gallery.zh.md画廊
- image/页面包
- hugo.yml站点配置(GitHub)
按平台分成标签页
围栏带 tab=(以及 group= value=)时成为一组标签页中的一页,可以与代码围栏混排。
- /etc/pigsty/配置
- /var/lib/pgsql/数据
- /usr/bin/pig可执行文件
- ~/Library/Application Support/pigsty/配置
- /opt/homebrew/bin/pig可执行文件
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-filetree">,可选标题栏,目录是原生 <details>;带注释时多一条可拖动分隔条(唯一的运行时) |
| 打印 | 同一棵树,全部展开,没有分隔条,注释换行不截断 |
| Markdown | 原样输出 filetree 围栏 |
| RSS | 围栏源码放进 <pre> |
窄屏(小于 sm 断点)时布局收成单列:注释移到名称下方,不再截断,分隔条隐藏。不带注释的树是单列,也不加载任何脚本。
参数参考
围栏属性(写在 ```filetree 后面):
title, ,- 树上方的标题栏;不写就不画;不能为空
tab, ,- 让这棵树成为一个标签页
group/value, ,- 标签页分组与同步值;必须与
tab同时出现 class, ,- 透传给站点 CSS
条目属性(写在每行末尾的 {…} 里):
icon, ,- 例如
fa-solid fa-lock;格式不符构建失败 tone, ,neutralinfosuccesswarningdanger,只给图标上色open, ,- 仅目录;
false表示初始收起 type, ,dir或file,覆盖自动判断
行语法本身:
缩进- 两个空格 / 四个空格 / 制表符 /
tree的│ ├── └──连线都行 - name- 项目符号可省略;
-*+等价 name/- 结尾斜杠表示目录;名字原样渲染,斜杠保留
[name](url)- 带链接的条目
# 注释- 第一个前面带空白的
#之后的内容;\#是字面井号 N directories, M filestree的统计行,自动丢弃
未知属性、未知取值、写在文件上的 open、格式错误的 {…}、退回到未打开过的缩进层级,都会构建失败,并给出围栏内的行号。
限制与常见问题
- 只有
filetree围栏这一种形态:没有{.filetree}列表标记,也没有 shortcode。 - 注释与名字都是纯文本:写
**粗体**会原样显示,围栏源码在任何环境里都读得通。 - 不读取磁盘:树是手写或粘贴的静态内容,不随仓库变化。
- 不提供搜索、多选、复制整棵树:需要逐字复制时用普通代码块。
- 分栏宽度不持久化:拖动过的位置刷新后回到构建期算出的默认值。
相关
10 - 公式
公式由 KaTeX 在构建期渲染成 HTML + MathML,页面只额外加载一份本地 KaTeX 样式表,没有 JavaScript,也不请求远程数学服务。行内公式写 \(…\),块级公式写 $$…$$、\[…\],另有 math 与 chem 两种围栏。需要 TikZ 绘图或 KaTeX 不支持的宏包时,改用预渲染的图片。
最简例子
行内公式写在句子中,前后的空格与标点留在分隔符外面。
共享缓冲区命中率是 ,其中 是 blks_hit, 是 blks_read。
块级公式
独占一段的公式用 $$ 包起来,居中显示,字号更大。\[…\] 是等价写法。
一棵扇出为 、共 个键的 B 树,其高度为:
一行装不下的长公式在正文列内横向滚动,不会把版面撑宽;打印时保持静态。
math 围栏
math 围栏是块级公式的另一种写法,不依赖站点的 passthrough 配置。源码在 GitHub 上是一个普通代码块。
上式是 Little 定律在连接池上的形式:稳态下需要的并发连接数等于到达速率乘以平均响应时间。连接池大小通常远小于客户端数量。
化学式与单位
chem 围栏使用 KaTeX 的 mhchem 扩展,正文写 \ce{…}。同一个扩展也能排物理单位。
语法见 mhchem 手册。
编号公式
块级公式下面跟一行属性即成为编号公式。num 是作者书写的字符串(3-1、5.3),主题不自动计数;#id 不写时默认为 eq-<num>。编号显示在公式右侧,前缀「公式」按站点语言本地化。
见公式 3-1:乘上保留天数就是归档盘容量的下限。
caption(纯文本)可以省略。#id 与 caption 必须与 num 同时出现,不存在「半编号」的公式。同一页里重复的 ID、或同一编号指向两个 ID,都会构建失败。
交叉引用
正文可以用普通链接引用编号公式,上一节即是这种写法。跨页引用、或需要自动带上「公式 N」标签时用 xref:
容量规划从 公式 3-1 开始。
xref 可以写在目标之前,前向引用合法。整本书的公式目录、book-equations 索引见书籍出版。
eq shortcode
eq 供无法开启 passthrough 的站点使用,正文交给同一个 KaTeX 渲染器。不带参数时是一个不注册编号的块级公式;带 num 时与上一节的属性行形态等价。
本站已开启 passthrough,日常写作用 $$。eq 用于迁移来的书稿与不能修改 hugo.yml 的场合。
站点前置配置
math 与 chem 围栏无需配置。$$、\[…\]、\(…\) 这些分隔符依赖 Goldmark 的 passthrough 扩展。Hugo 不合并主题的 markup 配置,这段必须写在站点自己的配置文件里。本站使用下面这份:
各键的完整定义见配置总览。分隔符不能与站点正文冲突:单个 $ 没有配进去,避免「$5」这样的价格被当成公式。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 构建期渲染好的 KaTeX HTML + MathML;本页额外加载一份本地 katex.min.css,没有公式的页面不加载 |
| 打印 | 同 HTML,静态,长公式不滚动 |
| Markdown | 原样输出源码:$$ 块(连同下面的属性行)、math / chem 围栏、\(…\);eq shortcode 输出 **公式 3-2.** 说明 + 一个 $$ 块 |
| RSS | 与 Markdown 相同的静态文本 |
任何形态都不加载 JavaScript。
参数参考
四种写法:
\(…\),- 由站点 passthrough 配置决定;不能带属性
$$…$$/\[…\],- 同上;可以跟一行属性变成编号公式
```math,- 不依赖 passthrough 配置;不接受属性
```chem,- 同上,正文写
\ce{…}
块级公式的属性行 {…}:
num, ,[0-9A-Za-z.-]+;注册为编号公式,右侧显示「公式 N」#id, ,[A-Za-z][A-Za-z0-9_.:-]*;锚点与交叉引用目标caption, ,- 编号后面的说明;需要
num
eq shortcode 的参数:
num, ,- 同上;不写就是一个不编号的普通块级公式
id, ,- 需要
num caption, ,- 需要
num class, ,- 需要
num;透传给站点 CSS 正文, ,- 必填,非空
TeX 写错(未知命令、括号不配对)会构建失败,报错里带 KaTeX 的信息与源码位置。
限制与常见问题
- 分隔符由站点配置决定:
$$、\[…\]、\(…\)是否渲染只取决于站点markup.goldmark的 passthrough 扩展。front matter 里写math: true主题不读,缺少配置时$$仍然原样显示;改用math围栏或eq可以绕开。 - 只有
$$块和eq能编号:math围栏不接受属性行,需要编号就换写法。 - 编号是手写的:主题不自动计数,也不重排;调整章节顺序要自己改
num。 - 行内公式不能带属性:属性行只对块级公式有效。
caption是纯文本:里面的 Markdown 不解析。
相关
11 - Mermaid
mermaid 围栏把文本写成流程图、时序图、甘特图、类图与状态图,本地渲染、跟随深浅色、diff 友好。mermaid 围栏把一段文本渲染成流程图、时序图、甘特图、类图、ER 图与状态图。图以源码形式存在,可以进 Git、可以 review diff、可以被搜索命中;渲染由主题自带的 Mermaid 在读者浏览器里完成,不请求外部服务。需要像素级控制的示意图画成 SVG,按图片使用。
最简例子
flowchart LR 内容["content/"] --> Hugo 配置["hugo.yml"] --> Hugo 主题["OINK 主题"] --> Hugo Hugo --> 站点["public/"]
围栏语言写 mermaid 即可,没有其它开关。主题检测到这个围栏后才把 Mermaid 运行时加入这一页,同一页里画十张图也只加载一次。
时序图
sequenceDiagram 描述参与者之间按时间发生的消息,适合说明请求链路与加载顺序。
sequenceDiagram autonumber participant 读者 as 读者浏览器 participant CDN as 静态托管 participant JS as 页面脚本包 读者->>CDN: GET /zh/docs/components/mermaid/ CDN-->>读者: HTML(含 <pre class="mermaid">) 读者->>CDN: GET 本页的脚本包 CDN-->>读者: mermaid.min.js JS->>JS: 把围栏源码渲染成 SVG Note over JS: 未使用的运行时不下载
甘特图
gantt 画时间区间。下面是 PostgreSQL 各大版本从发布日算起的五年社区支持期,1825d 即五年。
gantt title PostgreSQL 大版本的五年社区支持期 dateFormat YYYY-MM-DD axisFormat %Y section PG 15 发布于 2022-10-13 :2022-10-13, 1825d section PG 16 发布于 2023-09-14 :2023-09-14, 1825d section PG 17 发布于 2024-09-26 :2024-09-26, 1825d section PG 18 发布于 2025-09-25 :active, 2025-09-25, 1825d
类图与 ER 图
classDiagram 画类型与关系,erDiagram 画实体与基数。两者都常用来解释数据模型。
classDiagram
class Page {
+string Title
+string Description
+int Weight
+Content()
+OutputFormats()
}
class Resource {
+string Name
+string RelPermalink
+Resize(spec)
}
class OutputFormat {
+string Name
+string MediaType
}
Page "1" --> "0..*" Resource : 页面包资源
Page "1" --> "1..*" OutputFormat : html / print / markdown / rsserDiagram
pg_database ||--o{ pg_namespace : "包含模式"
pg_namespace ||--o{ pg_class : "包含关系"
pg_class ||--o{ pg_attribute : "包含列"
pg_class ||--o{ pg_index : "被索引"
pg_class {
oid oid PK
name relname
char relkind
}
pg_attribute {
oid attrelid FK
name attname
smallint attnum
}状态图
stateDiagram-v2 画状态与迁移条件。下面是 OINK 主题一次发布依次经过的五个状态。这五个状态互不等价,本地构建通过不属于其中任何一个。
stateDiagram-v2 [*] --> 源码完成 源码完成 --> 已验证 : 主题检查脚本 + 站点测试套件全绿 已验证 --> 已发布 : 推送不可变的签名 vX.Y.Z 标签 已发布 --> 已文档化 : 站点 go.mod 钉住该标签 已文档化 --> 已部署 : 生产构建上线 已部署 --> [*] 已发布 --> 源码完成 : 发现问题只能出新补丁版本,标签不移动
单张图的标题与配置
围栏正文最前面可以写 Mermaid 自己的 YAML 头,它不是 Hugo front matter。title 给图加标题,config 覆盖这一张图的 Mermaid 配置。写死 config.theme 的图不再跟随站点深浅色。
---
title: 只有用到的运行时才会进包
config:
flowchart:
curve: linear
---
flowchart TD
页面 --> 判断{用了什么组件?}
判断 -->|Mermaid 围栏| M[mermaid.min.js]
判断 -->|ECharts 围栏| E[echarts.min.js]
判断 -->|都没用| B[只有基础包]深浅色
页面初始化时主题读取当前配色模式:深色模式下用 Mermaid 的 dark 主题,浅色模式下用站点配置的主题。Mermaid 不支持重新初始化,读者切换配色时会 重载整个页面,图的配色随之更新。
因此不要把 Mermaid 图放进需要保留输入状态的页面,例如带表单的页面。
站点级默认写在 hugo.yml 里,键名小写,主题按 Mermaid 的默认配置匹配回正确的大小写:
完整键表见配置总览,可用值以 Mermaid 配置文档为准。
放进标签页与步骤
mermaid 围栏没有 tab 属性,相邻围栏标签页只对普通代码围栏生效。并排比较两张图用 tabs shortcode。
flowchart LR Markdown --> Goldmark --> 渲染钩子 --> HTML
flowchart LR 页面 --> HTML 页面 --> 打印 页面 --> Markdown 页面 --> RSS
{{% steps %}} 里的每一步是页面级 Markdown,其中可以写 mermaid 围栏,用法见步骤。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <pre class="mermaid"> + 本地 Mermaid 运行时,浏览器画成 SVG |
| 打印 | 与 HTML 相同:打印视图同样加载运行时,图会画出来 |
| Markdown | 原样保留 mermaid 围栏与它的源码 |
| RSS | 输出 <pre class="mermaid"> 包着的图表源码,订阅端看到的是文本 |
参数参考
围栏属性:没有。mermaid 围栏不读属性行,写 {height=…}、{class=…} 之类既不生效也不报错;尺寸由图自身与容器宽度决定。
站点参数(hugo.yml):
params.mermaid, ,- 整个映射按 Mermaid 的
initialize()配置传入;键名写小写,主题按 Mermaid 默认配置匹配回正确大小写 params.mermaid.theme, ,- 浅色模式下的主题;深色模式下被强制为
dark
单张图的配置写在围栏正文最前面的 YAML 头里(title、config),属于 Mermaid 语法,不是主题参数。
限制与常见问题
- 切换深浅色会重载页面:Mermaid 不支持重新初始化,主题在渲染正确与不刷新之间选择了前者。
- 图不能编号、不能缩放:Mermaid 输出的是内联 SVG,不是
<img>,{#id num=}编号与图片缩放都不适用;需要编号时导出成图片,按图片的编号写法使用。 - 围栏属性无效:宽度在图里控制(
flowchart的方向、classDiagram的布局),或者用 CSS。 - 语法错误只在浏览器里可见:Hugo 不解析 Mermaid 语法,写错的图在页面上显示 Mermaid 的报错框,构建照样通过,发布前要在浏览器里确认。
- RSS 订阅者只能看到源码:结论要写在正文里,不要只画在图上。
相关
12 - PlantUML
plantuml 围栏写时序图、类图、组件图、活动图与用例图;渲染必须由你自己配置一个 PlantUML 服务。plantuml 围栏里写 PlantUML 源码,浏览器把源码压缩编码后拼在一个 PlantUML 服务的 URL 后面,换回一张 SVG。适合需要完整 UML 表达力的时序图、类图、组件图、活动图与用例图。渲染依赖一个渲染服务:主题不提供默认端点,enable: true 却没给 svg_image_url 会让构建失败;没有可用服务时改用 Mermaid。
PlantUML 要连你自己的服务,本站不假设读者有哪个端点可用。当前主题版本的 plantuml 围栏还会把 <、>、&、" 二次转义,带箭头或引号的源码送到端点后返回 Syntax Error? 图(见限制与常见问题)。下面每段源码本身都是正确的 PlantUML。
编码后的图表源码作为 URL 发给你配置的端点。不要在 PlantUML 图里写口令、内网主机名或客户名称。内网站点自建端点,或改用预渲染的图片。
最简例子
时序图是 PlantUML 最常用的一类:participant 声明参与者,-> 是同步消息,--> 是返回。
画出来是四条泳道、四条消息的一张时序图:读者打开页面 → 浏览器带着编码后的源码请求端点 → 端点返回 SVG → 运行时把围栏替换成图片。
类图
class 写成员,"1" -- "0..*" 写关系基数,用来解释数据模型。
三个方框各带一列字段,两条带基数标注的连线:一个发布可以被多个订阅使用,每个订阅绑定一个复制槽。
组件图
package 圈出部署单元,[组件] 是方块,--> 是依赖方向。
两个虚线框,框里各三个组件方块,五条带标注的箭头串起采集链路。
活动图
start / stop 加 if … then … else … endif 画带分支的流程。这类图不含箭头字符,是当前版本里能正常渲染的一类。
一条竖向流程线,两个菱形判断各分出「是 / 否」两支,四个终点。
用例图
actor 是小人,(用例) 是椭圆,rectangle 圈出系统边界,适合放在文档的「读者是谁」一节。
左边三个小人,右边一个方框里七个椭圆,连线表示谁能做什么。
深色模式下的配色
服务端不知道站点的配色模式,渲染出来的 SVG 底色是固定的白色。skinparam backgroundColor transparent 去掉底色,图落在页面背景上。线条与文字设成中性色后,两种模式下都可读。
PlantUML 的 !theme 指令(例如 !theme plain)也可用,主题包由服务端提供,自建端点需要确认已安装。
渲染服务
围栏本身没有开关,能否渲染取决于站点配置:
enable: true却没写svg_image_url→ 构建报错params.plantuml.enable requires an explicit params.plantuml.svg_image_url。主题不代替站点选择公共服务。- 自建可以用官方镜像
plantuml/plantuml-server,svg_image_url指向它的/svg/路径,结尾的斜杠不能省略,编码后的源码拼在它后面。 - 端点的跨域策略、站点 CSP 的
img-src(svg: true时还有connect-src)都要放行;子路径部署时写绝对 URL。
这几个键的完整定义在配置总览。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 先输出 <pre><code class="language-plantuml"> 源码,启用后由运行时替换成 <img>(svg: true 时是 <svg data-src>) |
| 打印 | 与 HTML 相同:打印视图同样加载运行时并请求端点 |
| Markdown | 原样保留 plantuml 围栏与它的源码 |
| RSS | 只有围栏源码,订阅端看到的是文本 |
未启用、或运行时没有加载时,页面上留下的是一段可读的源码块,不会出现坏图标。
参数参考
围栏属性:没有。plantuml 围栏不读属性行;它也不走 OINK 的代码块外壳,title、copy、行号这些代码块参数在这里都无效。
站点参数(hugo.yml):
params.plantuml.enable, ,- 关闭时围栏保持为代码块,不加载运行时
params.plantuml.svg_image_url, ,- 渲染端点,编码后的源码直接拼在它后面;
enable: true时必填,否则构建失败 params.plantuml.svg, ,false插<img src>;true插<svg data-src>并额外加载外部 SVG 加载器,SVG 内容进 DOM、可被 CSS 影响
主题只读这三个键,其它键写了没有效果。
限制与常见问题
<、>、&、"会被二次转义:当前主题版本的plantuml围栏对内容多做了一次转义,页面上留下-->、"这样的字面文本,端点收到后返回一张Syntax Error?图。带箭头的图(时序、组件、用例、状态)目前渲染不出来,只有活动图这类不含这些字符的能正常渲染。修复前请改用 Mermaid 或预渲染的图片。- 必须有服务:主题不提供、也不默认任何公共端点。
- 图表源码会离开浏览器:涉密内容不要写进 PlantUML 围栏。
- 不跟随深浅色:服务端不知道读者的配色模式,只能靠
skinparam自己调。 - 不能编号、不能缩放:运行时插入的
<img>不经过图片渲染钩子,{#id num=}与图片缩放都用不上。
相关
13 - 思维导图
markmap 围栏把一段 Markdown 大纲变成可展开、可缩放的思维导图,源码本身就是能读的提纲。markmap 围栏的正文是一段普通的 Markdown 大纲:标题与列表决定层级,浏览器把它画成一棵可展开、可折叠的树。适合把「这一节讲了什么」的层级一次呈现。节点之间有方向、有条件的流程用 Mermaid。
最简例子
# OINK
## 本地优先
- 运行时全部随主题分发
- 不依赖任何 CDN
## Markdown 原生
- 组件是围栏和属性行
- 不写 shortcode 也能用
## 四态输出
- HTML
- 打印
- Markdown
- RSS一级标题是根节点,其余标题与列表项按缩进挂在它下面。点击节点上的圆点折叠或展开这一支,鼠标滚轮缩放,拖动平移。右下角一排工具按钮提供缩放、适应窗口与下载 SVG。
多层级
层级越深字号越小,画布自动排布。下面是本站文档的六个栏目与它们的页数。
# OINK 文档
## 简介(4 页)
### 它是什么
### 功能一览
### 案例
### 许可
## 快速上手(3 页)
### Fork 本站
### 目录结构
### 从零开始
## 创作内容(8 页)
### 组织内容
### 编写页面
### 页面参数
### 博客
### 书籍
### 发布与下载
### OpenAPI
## 组件(22 页)
### 提示块 / 标签页 / 步骤 / 卡片
### 图片 / 画廊 / 表格 / 参数表
### 图表:Mermaid / PlantUML / 思维导图 / ECharts
## 定制站点(15 页)
### 品牌 / 导航 / 搜索 / 多语言
### 首页 / 版本 / 分类 / 打印
## 维护管理(7 页)
### 预览 / 部署 / 升级
### 评论 / 统计 / 排错链接、代码与强调
节点里可以写行内 Markdown:链接可点击,行内代码用等宽字体,粗体与斜体照常生效。
# 日常命令
## 预览
- `hugo server` — 打开 [localhost:1313](http://localhost:1313/)
- `hugo server -D` — **连草稿一起**预览
## 构建
- `hugo --printPathWarnings --panicOnWarning`
- `hugo --gc --minify` — 发布用
## 主题
- `hugo mod get -u github.com/pgsty/oink`
- [主题仓库](https://github.com/pgsty/oink)
- [本站源码](https://github.com/pgsty/oink.pgsty.com)节点里的公式
Markmap 运行时带了一份本地 KaTeX,节点里的 $…$ 会被渲染成公式。
# 常看的几个 PostgreSQL 指标
## 缓存命中率
- $\frac{blks\_hit}{blks\_hit + blks\_read}$
- 低于 0.99 时检查 shared_buffers
## 复制延迟
- $lsn_{primary} - lsn_{replica}$
## 事务吞吐
- $TPS = \frac{\Delta xact\_commit}{\Delta t}$控制初始展开层数
围栏正文最前面可以写一段 Markmap 自己的 YAML 头,它不是 Hugo front matter。initialExpandLevel 只展开前几层,其余分支由读者点开。colorFreezeLevel 指定从第几层起同一分支使用同一种颜色。
---
markmap:
initialExpandLevel: 2
colorFreezeLevel: 2
---
# 主题仓库的检查脚本
## 源码级契约
### check-i18n.py
### check-taxonomy.py
### check-font-tokens.py
## 输出级检查
### check-output.py
### check-goldens.py
### check-code-blocks.py
### check-content-primitives.py
### check-media-primitives.py
## 浏览器运行时
### node --test tests/js/**/*.test.js折进折叠块
每张导图固定 300 像素高,正文里连着放三张会占掉大量版面。把全景图折进 > [!DETAILS],由读者自己展开。折叠块里的每一行都要以 > 开头,围栏也不例外。
# pgsty/oink
## layouts/
- baseof.html 与各类型的壳
- _partials/shell/
- _markup/ 渲染钩子
- _shortcodes/
## assets/
- scss/ 令牌与组件样式
- js/ 浏览器运行时
- third_party/ 随主题分发的库
## i18n/
- 32 个语言文件,键完全对齐
## docs/
- 冻结契约文档输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 先输出 <pre><code class="language-markmap">,运行时把它换成 <div class="markmap"> 并画出 SVG |
| 打印 | 与 HTML 相同:打印视图同样加载运行时 |
| Markdown | 原样保留 markmap 围栏与它的大纲源码 |
| RSS | 只有大纲源码,订阅端看到的是一段可读的提纲 |
大纲本身就是内容:拿不到 JavaScript 的地方读到的仍是完整层级。
参数参考
围栏属性:没有。markmap 围栏不读属性行,高度由主题固定为 300px(.markmap > svg),宽度撑满正文栏。
站点参数(hugo.yml):
params.markmap, ,- 关闭时围栏保持为代码块,不加载任何运行时
键的完整定义见配置总览。每张图的行为写在围栏正文最前面的 markmap: YAML 头里(initialExpandLevel、colorFreezeLevel、maxWidth 等),属于 Markmap 语法,可用键以 Markmap 文档为准。
限制与常见问题
- 输出是固定 300px 高的内联 SVG:高度由一条
.markmap > svg规则统一,围栏改不了,层级太多时用initialExpandLevel收起或拆成两张图;内联 SVG 也不适用{#id num=}编号与图片缩放。 - 不跟随深浅色:连线颜色由 Markmap 自己的调色板决定,两种模式下都需要检查对比度。
- 没开
params.markmap就只是代码块:不用这个组件的站点不加载任何运行时。 - 右下角工具栏里的「下载 SVG」是浏览器行为,导出的是当前展开状态的快照。
- 大纲里避开
<、>、&、":当前主题版本的markmap围栏会把这几个字符二次转义,节点上会出现>、"这样的字面文本;写链接用[文字](URL),不要用尖括号自动链接。
相关
14 - Draw.io
.drawio.svg 当普通图片放进页面,读者鼠标移上去就能点开 Draw.io 编辑器改图。Draw.io 集成没有围栏也没有 shortcode,用的是普通 Markdown 图片。Draw.io 导出时勾上「Include a copy of my diagram」,SVG 或 PNG 里会带一份 mxfile 源码;主题的运行时识别这份副本后,给图片加一个编辑按钮。适合需要读者取走修改的图;只用于展示的图按普通图片处理。
最简例子
写法与普通图片相同,文件名不受限制,.drawio.svg 只是惯例。
这张图嵌着一份 mxfile 副本,因此被包进了 .drawio 容器。鼠标移到图上时,右下角出现一个铅笔按钮;点击后在当前页面盖一层全屏 iframe,加载站点配置的编辑器。
副本检测
运行时的判断依据只有一条:文件内容里有没有 mxfile 字样,与文件名无关。下面这张同样是 SVG、同样是块级图片,但它是手写的,没有副本,也就没有按钮。
带图注
Draw.io 图片走的是普通图片渲染钩子,图片的属性照常可用。加 caption 得到带图注的 figure,编辑按钮仍然出现在图上。
编号成书里的图
加 {#id num=…} 得到一张可交叉引用的编号图,与别的图片一样能被 xref 引用、进入图目录。
编号与交叉引用的完整规则见书籍出版。
SVG 还是 PNG
两种都识别。Draw.io 导出 PNG 时同样能带上副本,存在 PNG 的文本块里,运行时的判断逻辑相同。

文档里优先用 SVG:缩放不失真,文字是真实文本(可被搜索、可被读屏器读取),改动的 diff 也读得懂。图特别复杂、或目标平台不支持 SVG 时用 PNG。只有 PNG 能走 Hugo 的图片处理;SVG 上的处理操作会告警并保留原图,严格构建会拒绝该告警。
编辑流程
按钮依次做三件事。
盖一层遮罩
页面上插入一个全屏的 div.drawioframe,里面是一个 iframe,地址是配置的 drawio_server 加上一串固定参数(embed=1&ui=atlas&proto=json&saveAndEdit=1&noSaveBtn=1)。
把图送进编辑器
编辑器就绪后,运行时把这张图片的内容(含 mxfile 副本)作为 data URL 发进 iframe。这一步不经过你的服务器。
保存与回写
在编辑器里点保存,运行时让编辑器按原格式(SVG 或 PNG)导出,由浏览器下载成同名文件。运行时不写回仓库:把下载到的文件覆盖 content/ 里那一份,再自行提交。
编辑按钮供读者取走图去改,不是站点的在线编辑功能。
编辑器地址
enable: true却没写drawio_server时会告警并关闭编辑;严格构建会因该告警失败。主题不代替站点选择公共服务。- 编辑过程必须留在组织内部时,部署一份自托管编辑器,把地址指向它。
- 公共端点
https://embed.diagrams.net/可用,读者的图会进入第三方页面。
这两个键的完整定义在配置总览。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 普通 <img>(或 <figure>);启用后运行时把带副本的图包进 <div class="drawio"> 并加按钮 |
| 打印 | 图片照常打印;按钮默认隐藏(只在悬停时出现),打印上不会有它 |
| Markdown | 普通 Markdown 图片语法 |
| RSS | 普通 <img>,绝对 URL,没有按钮 |
图片本身在四态里都在,编辑按钮是增量能力。
参数参考
没有专属的围栏或 shortcode 参数。图片属性行沿用图片那一套(caption width height link #id num command options)。
站点参数(hugo.yml):
params.drawio.enable, ,- 关闭时不加载任何脚本,图片就是图片
params.drawio.drawio_server, ,- 编辑器地址;
enable: true时必填
限制与常见问题
- 运行时只在渲染内容含
.svg或.png候选图的页面加载;同一 URL 的图片合并检查,只读取一次以查找mxfile。 - 导出时忘了勾「Include a copy of my diagram」,图就只是一张图,没有按钮。
- 编辑依赖编辑器,且不写回仓库:离线环境里图片正常显示,按钮点了没有反应;编辑器保存等于浏览器下载,替换文件与提交都要手动做。
- 按钮只在悬停时出现:触屏设备上没有 hover,读者不容易发现它,不要把可编辑当成关键功能来讲。
- 配色不跟随深浅色:导出的 SVG 颜色是固定的;把填充设成
none、线条与文字用中性灰,两种模式下都能看(本页这两张图就是这么做的)。
相关
15 - ECharts
echarts 围栏里用 YAML 或 JSON 写图表选项,Hugo 构建期校验,浏览器用本地 ECharts 画出跟随深浅色的统计图。echarts 围栏的正文是一段 YAML 或 JSON 的 ECharts 选项对象,不是代码。适用于需要坐标轴、序列与图例的定量图表;只表达关系与流程时用 Mermaid,只表达顺序与层级时用 Infographic。Hugo 在构建期解析选项,解析失败则构建失败;浏览器用随主题分发的 ECharts 绘图,只有用到它的页面加载运行时。
最简例子
一个柱状图只需要三段:xAxis、yAxis、series。下面是本站文档六个栏目各有多少页。
tooltip:
trigger: axis
xAxis:
type: category
data: [简介, 快速上手, 创作内容, 组件, 定制站点, 维护管理]
yAxis:
type: value
name: 页数
series:
- name: 页数
type: bar
data: [4, 3, 8, 22, 15, 7]两种格式都接受,YAML 不需要引号与逗号,写起来更短。缩进写错、正文解析成数组而不是映射,构建在这一行失败,不会输出一张空白图。
多序列折线
series 是数组,多一项就是多一条线;legend 让读者单独隐藏其中一条。下面是 PostgreSQL 各大版本的发布年份,以及按社区五年支持策略推算的终止年份。
tooltip:
trigger: axis
legend:
data: [发布年份, 支持终止]
grid:
left: 56
right: 24
top: 48
bottom: 40
xAxis:
type: category
name: 大版本
data: ["9.6", "10", "11", "12", "13", "14", "15", "16", "17", "18"]
yAxis:
type: value
min: 2015
max: 2031
name: 年份
series:
- name: 发布年份
type: line
smooth: false
data: [2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025]
- name: 支持终止
type: line
lineStyle:
type: dashed
data: [2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030]版本号要加引号:YAML 里不带引号的 10 是数字,9.6 也是;作为分类轴的标签它们必须是字符串。
饼图与环形图
radius 给两个值就是环形图。下面是 OINK 的 29 个 shortcode 按用途的构成。
tooltip:
trigger: item
formatter: "{b}:{c} 个({d}%)"
legend:
bottom: 0
series:
- type: pie
radius: [42%, 70%]
itemStyle:
borderRadius: 6
borderWidth: 2
label:
formatter: "{b} {c}"
data:
- { value: 14, name: 核心组件 }
- { value: 10, name: Book 编号与索引 }
- { value: 3, name: 发布与下载 }
- { value: 2, name: OpenAPI }{b} {c} {d} 是 ECharts 的模板占位符(名称 / 数值 / 百分比),写在字符串里即可,不需要函数。
高度与通栏
height 默认 400px,接受 px rem em vh vw %;full=true 去掉正文的宽度限制,让图铺满内容区。适用于数据点多、标签长的图。
tooltip:
trigger: axis
grid:
left: 40
right: 16
top: 24
bottom: 32
xAxis:
type: category
data: [i18n, 分类法, 字体令牌, 内容契约, 导航, 运行时, 侧栏图标, 搜索, 动作, 命令面板, 双语文档, 阅读, 发布物, 下载, Landing, Book, 迁移, 键盘, 页尾, 输出, 金样本]
yAxis:
type: value
name: 脚本数
series:
- type: bar
data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]无效的高度(360、36pt)会让构建失败,不会退回默认值。
深浅色
不写 theme 时,图按读者当前的配色模式初始化;切换配色时图原地重绘,不刷新页面。容器尺寸变化时自动 resize。把本页切到深色,上面每张图的底色与文字随之改变。
写定 theme 则固定配色,两种模式下都是同一套:
xAxis:
type: category
data: [HTML, 打印, Markdown, RSS]
yAxis:
type: value
series:
- type: bar
data: [1, 1, 1, 1]运行时内置的只有 dark;其它 ECharts 主题要先用 echarts.registerTheme() 注册才能在这里引用。没有品牌要求时不写 theme,让图跟随站点配色。
回调:$fn:
围栏是数据,不能带 JavaScript。某个选项需要函数时(提示框格式化、数据驱动的颜色),在选项里写字符串 "$fn:名字",再把这个名字注册到 window.OinkEchartsFunctions:
tooltip:
trigger: axis
formatter: "$fn:pageShare"
xAxis:
type: category
data: [简介, 快速上手, 创作内容, 组件, 定制站点, 维护管理]
yAxis:
type: value
series:
- type: bar
data: [4, 3, 8, 22, 15, 7]鼠标悬停在任意一根柱子上,提示框里是该函数拼出的句子。名字未注册时该选项解析为 undefined,图按未设置该项绘制,构建与运行都不报错。脚本与围栏放在同一页的相邻位置,便于一起改动。
这段脚本属于站点代码,按代码审查对待。字符串模板({b} {c} {d})能表达的格式不写成函数。
数据位置
围栏正文是字面量。Hugo 不在其中展开 shortcode、front matter 变量或 data/ 目录里的文件,数字写在围栏里。代价是数据不能共享,收益是图表源码与数据一起进入 Git,diff 能看出改动了哪个数值。
数据经常变动(版本矩阵、发布物清单)时不做成图:改用表格,或发布与下载页中由 data/ 驱动的组件。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-echarts"> 里一个画布容器加一段 application/json 选项,本地 ECharts 画图 |
| 打印 | 不画图,输出 <pre class="td-echarts-source"> 包着的围栏源码 |
| Markdown | 原样保留 echarts 围栏与选项源码 |
| RSS | 与打印相同,只有源码 |
图上的结论要在正文里写一遍:打印与 RSS 输出里没有图。
参数参考
围栏属性行(```echarts {…}):
height, ,- 只接受非负数字加
pxrememvhvw%;其它写法构建失败 theme, ,- 固定使用某个 ECharts 主题,从此不再跟随站点配色;内置只有
dark full, ,true去掉正文宽度限制,图铺满内容区class, ,- 透传给容器,交给站点 CSS
style、on* 与其它未知属性都会让构建失败。围栏正文必须能解析成一个 YAML/JSON 映射,解析失败或解析成数组同样失败。选项键本身是 ECharts 的,以官方选项手册为准。
没有站点级参数:ECharts 不需要在 hugo.yml 里开关,用到时才加载。
限制与常见问题
- 围栏里不能写 JavaScript:需要函数时通过
$fn:桥接,未注册的名字解析为undefined,没有报错。 - 围栏不读外部数据:
data/目录、front matter 与 shortcode 都引用不到,数字写在围栏里。 - 打印与 RSS 里只有源码,结论要写进正文。
- YAML 的类型转换:分类轴上的
10、9.6、on、yes会被解析成数字或布尔值,需要引号。 - 颜色不是唯一的区分手段:多序列图同时区分线型或标记形状,两种配色模式下都要检查图例对比度。
相关
- Infographic — 表达结构与顺序的信息图,不是统计图
- 表格 — 数据少、需要精确读数时用表格
- Mermaid — 关系图与流程图
- 代码块 — 围栏属性行的通用规则
16 - Infographic
infographic 围栏挑一个 AntV 模板,把标题与条目渲染成流程、时间线、漏斗、网格或层级信息图。infographic 围栏挑一个 AntV 模板,把「标题 + 一串条目」渲染成信息图。适用于表达顺序、层级与对比这类结构。需要坐标轴与数值精度时用 ECharts,需要条件分支的流程时用 Mermaid。围栏正文是数据,在 GitHub 上仍是一段可读的文本。
最简例子
第一行是 infographic 模板名,其后是一个 data 块:title 是标题,items 下面每个条目至少要有 label。
infographic list-row-simple-horizontal-arrow
data
title 一次文档改动的三步
items
- label 写
desc 先写中文 .zh.md
- label 校
desc 构建零告警,例子真渲染
- label 发
desc 补英文对等页,提交 PR缩进决定结构,两个空格一级。标签要短,说明放 desc。
时间线
sequence-timeline-* 系列把条目排成一条时间轴,label 是时间点,desc 是事件。
infographic sequence-timeline-simple
data
title PostgreSQL 近五个大版本
items
- label 2021
desc 14:并行查询与逻辑复制的一轮改进
- label 2022
desc 15:MERGE 语句
- label 2023
desc 16:逻辑复制可以从备库进行
- label 2024
desc 17:增量备份与 JSON_TABLE
- label 2025
desc 18:异步 IO 子系统漏斗
sequence-funnel-simple 画逐步收窄的阶段。下面是主题的五个发布状态:互不等价,走完最后一个才是上线。
infographic sequence-funnel-simple
data
title 一次主题发布要经过的五个状态
items
- label 源码完成
desc 代码写完,仅此而已
- label 已验证
desc 主题检查脚本与站点测试套件全绿
- label 已发布
desc 不可变的签名标签,能从 Go 代理拉到
- label 已文档化
desc 文档站钉住了这个标签
- label 已部署
desc 生产环境运行的就是这个版本网格卡片
条目之间没有先后关系时用 list-grid-*,它把条目排成网格而不是队列。
infographic list-grid-compact-card
data
title 同一页内容的四种输出
desc 每个内容组件都要在这四态里给出可用的结果
items
- label HTML
desc 交互式,按需加载运行时
- label 打印
desc 折叠展开,去掉缩放与复制按钮
- label Markdown
desc 纯文本,按字节比对金样本
- label RSS
desc 静态,与打印同源带数值的条目
条目上加 value,能表达比例的模板(饼、环、进度)会用到它。
infographic chart-pie-donut-plain-text
data
title 29 个 shortcode 的构成
items
- label 核心组件
value 14
- label Book 编号与索引
value 10
- label 发布与下载
value 3
- label OpenAPI
value 2层级与手绘风格
条目下面可以再嵌 children,hierarchy-mindmap-* 把它画成两层的结构图。顶层的 theme 块换整张图的风格,type 取 light、dark 或 hand-drawn。
infographic hierarchy-mindmap-level-gradient-compact-card
theme
type hand-drawn
data
root
label 主题仓库
children
- label layouts
desc 模板
children
- label _markup
desc 渲染钩子
- label _partials
desc 外壳与工具
- label assets
desc 资源
children
- label scss
desc 令牌与组件样式
- label js
desc 浏览器运行时
- label third_party
desc 随主题分发的库theme 属于 DSL,不是围栏属性。它不跟随站点的深浅色:写 type dark 的图在浅色页面上也是深底。两种配色模式下都要检查对比度。
挑模板
模板名是 结构-变体 的组合,同一个结构有多个视觉变体。常用的几类:
| 结构前缀 | 表达什么 | 例子 |
|---|---|---|
list-row-* list-column-* | 一排 / 一列并列的条目 | list-row-simple-horizontal-arrow |
list-grid-* | 网格,条目之间无先后 | list-grid-compact-card list-grid-badge-card |
list-pyramid-* sequence-funnel-* | 逐层收窄 | sequence-funnel-simple |
sequence-timeline-* sequence-roadmap-vertical-* | 时间线与路线图 | sequence-timeline-simple |
sequence-steps-* sequence-snake-steps-* | 有序步骤 | sequence-steps-simple |
compare-binary-horizontal-* compare-quadrant-* | 二元对比与四象限 | compare-binary-horizontal-simple-vs |
hierarchy-mindmap-* hierarchy-structure-* | 层级(配合 children) | hierarchy-mindmap-level-gradient-compact-card |
chart-pie-* chart-bar-* chart-column-* | 带 value 的示意图 | chart-pie-donut-plain-text |
relation-network-* relation-dagre-flow | 网络与流向(配合 relations) | relation-dagre-flow |
选能表达清楚关系的最小形式。完整图库见 AntV Infographic 图库,模板名与随主题分发的版本一一对应。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-infographic"> 里一个画布容器加一段 DSL,本地 AntV 运行时画成 SVG |
| 打印 | 不画图,输出 <pre class="td-infographic-source"> 包着的 DSL 源码 |
| Markdown | 原样保留 infographic 围栏与 DSL |
| RSS | 与打印相同,只有源码 |
图上的信息要在正文里写一遍:打印与 RSS 输出里只有那段 DSL。
参数参考
围栏属性行(```infographic {…}):
height, ,- 非负数字加
pxrememvhvw%;其它写法构建失败 full, ,true去掉正文宽度限制class, ,- 透传给容器
style、on* 与未知属性让构建失败;空的 DSL 正文也让构建失败。
DSL 的顶层键(属于 AntV,不是主题):
infographic/template- 模板名,第一行
datatitle、desc、items(也可以是sequencescomparesnodesvaluesrelationsroot,取决于模板结构)、orderthemetype(light/dark/hand-drawn)、palette、colorPrimary、stylize等width/height- DSL 层的画布尺寸,一般交给围栏属性
height design- 逐部件的细调,少用
items 里每个条目可用 label、desc、value、icon、children、group、id。DSL 的完整定义以 AntV Infographic 文档为准;随主题分发的版本与校验值记在主题仓库的 VENDOR.json 里。
限制与常见问题
- 模板名写错不会让构建失败:Hugo 只检查围栏属性,DSL 由浏览器运行时解析,模板不存在时容器里显示一行错误文字。改动模板名后在页面上确认。
- 不跟随深浅色:
theme写在 DSL 里,两种配色模式下都要检查对比度。 - 打印与 RSS 里只有 DSL,关键结论要写进正文。
- SVG 不是语义结构:屏幕阅读器读到的顺序未必是排版顺序。标题、列表、表格能表达的内容优先用它们。
- 标签要短:长文本在窄屏下会被截断或挤压,改动后在手机宽度下确认。
相关
17 - 画廊
gallery 围栏把一组相关截图排成响应式网格,每张可带说明或链接,并复用页面的图片缩放对话框。画廊(Gallery)把一组相关图片排成响应式网格,围栏里每行一张图。适用于同一件事的几个视图:几张截图、几种状态、几套配色。单张图用图片;相互之间没有顺序与对比关系的图片不适合放进同一个画廊。
最简例子
围栏里一行一张图,语法是 Markdown 的 。
替代文字必须写:它是这一项的标题、读屏器唯一能读到的文字,也决定这张图是否参与缩放。列数没有参数,网格随容器宽度自适应,窄屏减列。
加说明
图片后面用 # 起头写说明,显示在图下方。说明是纯文本,里面的 Markdown 按字面显示;要一个字面井号写 \#。

默认外壳:侧栏、正文、目录

OINK 的上游 Docsy,内容模型一脉相承

发布页由 data/download 里的事实生成,不联网
说明长短可以不一致:网格按最高的一项对齐,说明换行不影响相邻的图。图片先被解析,替代文字与路径里的 # 不需要转义。
每项一个链接
行尾的 {link=…} 让这一项成为链接,站内路径、相对路径、http(s): 都可以。
带链接的项不参与缩放,点击已有别的含义。同一个画廊里两种项可以混排:有链接的打开页面,没有链接的打开大图。
图片来源
来源解析顺序与普通图片一致:页面资源(页面包里的同目录文件)→ 全局资源 assets/ → 静态路径 /images/… → 远程 URL。本地资源带上固有尺寸,加载时不跳版;远程图构建期不下载,也取不到尺寸。

assets/images/… 下的图,可以做构建期处理

static/images/… 下的图,原样发布
页面资源与全局资源找不到时构建失败;静态路径与远程 URL 不检查存在性。
装饰图与缩放
替代文字留空表示这是装饰性图片:没有标题,读屏器跳过,也不参与缩放。
图片缩放是站点级开关,默认关闭。本页在 front matter 中开启了它,上面每张有替代文字、没有链接的图都可以点开看大图(Esc 关闭,焦点回到原处)。

装饰性配图,不参与缩放

有替代文字,可以点开
画廊没有自己的缩放运行时,复用整页共用的那个对话框。页面上没有可缩放的图时,运行时不加载。细节见图片 · 缩放。
加 class 与分标签页
class 可以加在整个围栏上(写在语言后面)或某一项上(行尾),主题不解释它,原样透传给站点 CSS。围栏带 tab=(以及 group= value=)时成为一组标签页里的一页。

默认配色

跟随系统或手动切换
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <ul class="td-gallery">,每项一个 <li>;符合条件的图带 data-td-image-zoom 标记;全部懒加载 |
| 打印 | 同一组图堆叠排列,没有缩放标记 |
| Markdown | 原样输出 gallery 围栏 |
| RSS | 与打印相同的静态堆叠 |
画廊不加载 JavaScript。
参数参考
行语法  [# 说明] [{key=value …}]:
,- 必须顶在行首。
alt是这一项的标题;留空表示装饰图 src,- 页面资源 / 全局资源 / 静态路径 / 远程 URL
# 说明,- 纯文本,显示在图下方;
\#是字面井号;不能为空 {link=…},- 让这一项成为链接,因而不可缩放
{class=…},- 给这一项加站点 CSS class
围栏属性:
tab, ,- 让这个画廊成为一个标签页
group/value, ,- 标签页分组与同步值;必须与
tab同时出现 class, ,- 透传给站点 CSS
没有 columns、caption、title 属性。行首不是图片、# 之外的尾随文字、空说明、未知属性、格式错误的 {…} 都会让构建失败,报错给出围栏内的行号。
限制与常见问题
- 只有围栏一种形态:没有
{.gallery}列表标记,也没有 shortcode。代价是源码在 GitHub 上不渲染成图片,收益是四态输出与缩放资格由主题保证。 - 不能指定列数,也不裁成统一宽高比:网格按视口自适应,图片按原始比例排列。
- 没有幻灯片、轮播与上一张 / 下一张:缩放对话框一次显示一张。
- 不下载远程图:构建期没有网络请求,远程图在浏览器加载前尺寸未知,可能跳版。
- 说明不解析 Markdown:需要富文本时写在画廊下方的段落里。
相关
18 - 徽章
徽章(Badge)是紧跟在名字旁边的行内状态标签:Beta、已弃用、v0.5、需自建服务。适用于一两个词能说完的状态;作者只选语义 tone,颜色由主题决定,浅色与深色模式下的对比度都有保证。状态需要解释、操作步骤或截止日期时,改用正文或提示块。
最简例子
text 是唯一必填参数,必须是非空字符串。
五种 tone
只有这五个取值,没有自定义颜色。
默认 信息 已支持 实验性 已弃用
不写 tone 时使用 neutral。其它取值让构建失败,报错给出源文件位置。
夹在句子里
徽章是行内元素,跟在名字后面,不占单独一行。
params.ui.image_zoom 默认关闭 打开后,
有替代文字的块级图片可以点开看大图。PlantUML 需自建服务
与 Draw.io 需自建服务 没有配置服务端点时会让构建失败,
而不是连接公共服务。
标题旁边
标题里不要写 shortcode。 Hugo 先生成目录、后替换 shortcode,所以徽章在标题上渲染正常,目录里却会留下一段 Hugo 的内部占位符文本。把状态写进标题下面的第一段:
OpenAPI 页面
0.5 新增 徽章紧跟在标题下方,目录保持干净, 锚点链接分享出去也不会带上徽章文字。
表格单元格里
对照表里用徽章标状态,比整列写「是」「否」更容易扫读。
| 组件 | 形态 | 状态 |
|---|---|---|
| 提示块 | > [!NOTE] | 稳定 |
| 画廊 | ```gallery 围栏 | 稳定 |
| PlantUML | ```plantuml 围栏 | 需自建服务 |
image shortcode | — | 已移除 |
列表与步骤里
- 安装 Hugo Extended ≥ 0.160.1
- 克隆文档站,修改
hugo.yml里的baseURL hugo server预览 1313 端口
卡片里
卡片有自己的 badge 参数(纯文本,固定在标题右侧);卡片正文里可以放徽章 shortcode。
一行 hugo mod get 完成安装 需要 Go
不联网的机器也能构建 手动升级
可点击的徽章
加 link 后徽章变成链接(<a>),站内路径、相对路径、http(s):、mailto: 都可以。
链接非法(协议不在白名单里)会让构建失败。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | 无链接时 <span class="td-badge td-badge--<tone>">,有链接时 <a class="td-badge …"> |
| 打印 | 同 HTML,静态行内元素 |
| Markdown | **Beta**,有链接时 [**Beta**](/…) |
| RSS | 同打印 |
不加载 JavaScript。徽章不是实时状态区域,新增徽章不会触发读屏器播报。
参数参考
text, ,- 必填,非空。读者看到的文字
tone, ,neutralinfosuccesswarningdangerlink, ,- 设置后徽章变成链接
只接受命名参数。没有 icon、class、color、outline、size 参数;写了未知参数、空 text、非法 tone 或非法链接都会让构建失败。
限制与常见问题
- 颜色不是唯一的含义载体:tone 是补充,文字要自己说清楚。
{{< badge text="🔴" >}}对读屏器没有信息。 - 没有图标参数:需要图标时改用卡片或提示块。
- 文字要短:徽章不换行地跟在名字后面,超过五六个字的内容写进正文。
- 同一处不超过三枚:连排的徽章会盖过它修饰的名字。
- 徽章只有 shortcode 一种形态,没有原生 Markdown 写法;纯 Markdown 阅读器里它退化成加粗文字。
相关
19 - 按键
kbd 写快捷键:一个 shortcode 接一串按键名,输出语义化的按键序列,打印与 Markdown 输出里同样可读。按键(Kbd)把读者要按下的键与正文区分开。适用于快捷键与组合键:一个按键一个位置参数,主题负责画框、补分隔符,并给读屏器一个可读的序列。命令名、选项名与要输入的文本用行内代码,它们不是物理按键。
最简例子
按 Ctrl 加 K 打开命令面板。
参数必须加引号,一个按键一个参数。少于一个按键、空字符串、命名参数都会让构建失败。
单个按键
一个参数对应一个键,符号键按原样写。
Escape 关闭对话框; / 进入搜索; t 切换亮色 / 暗色; l 循环切换语言。
组合键
多个参数按顺序渲染,中间补 +。这个加号对辅助技术隐藏,读屏器读到的是本地化的连接词。
⌘ 加 Shift 加 P 与 Ctrl 加 Shift 加 P 是同一个动作。 需要按字面的加号时,把它当成独立的一个按键:Ctrl 加 + 放大页面。
平台差异
按键名写读者键盘上印的标签:macOS 写 ⌘,Windows / Linux 写 Ctrl。不要把两个平台合进同一个序列,Ctrl/⌘ 这类写法读屏器无法正确朗读。在句子里说明平台,或分成标签页。
macOS 按 ⌘ 加 K,Windows 与 Linux 按 Ctrl 加 K。
快捷键表
速查表是按键最常见的位置。下面是本站生效的一部分全局键:
| 按键 | 作用 |
|---|---|
| Ctrl 加 K | 打开命令面板(macOS 是 ⌘ 加 K) |
| / | 面板的完整搜索态 |
| t | 切换亮色 / 暗色 |
| q / e | 上一篇 / 下一篇 |
| w s a d | 在侧栏树里上下移动、折叠、展开 |
| Escape | 从侧栏树回到正文 |
全站快捷键的完整清单见键盘导航。
步骤里
- 按 Ctrl 加 K 打开命令面板
- 输入
>进入纯命令态,或输入关键词搜索 - 用 ↑ ↓ 选中一项,Enter 前往
- Escape 关闭,焦点回到按下之前的位置
原始 <kbd> 标签
Markdown 里写原始的 <kbd> 标签得到同样的样式,GitHub 也这么渲染。区别是分隔符与无障碍序列要自己维护:单个键两种写法都可以,组合键用 shortcode。
按 F5 刷新;在编辑器里按 Ctrl+S 保存。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <span class="td-kbd-sequence"> 包着每个键一个 <kbd>;可见的 + 对读屏器隐藏,另有一个本地化连接词 |
| 打印 | 同 HTML,静态 |
| Markdown | 纯文本 Ctrl + K、⌘ + Shift + P |
| RSS | 同打印 |
没有 CSS 与 JavaScript 时,操作说明仍然可读。
参数参考
位置参数 1..n, ,- 至少一个,每个都必须非空且加引号;顺序就是显示顺序
只接受位置参数。没有 separator、label、platform、class、size 这些命名参数:Hugo 的 shortcode 不允许在一次调用里混用位置参数与命名参数。
限制与常见问题
- 一个序列表示同时按下的一组键:先按 A 再按 B 这类连续操作写成两个 kbd 加一句说明(先按 Escape,再按 Enter)。
- 不做平台检测:页面不会按访客的操作系统把
Ctrl换成⌘。 - 不做按键映射与录制:菜单路径、手势、游戏杆不在范围内。
- 漏写引号会让构建失败:
{{< kbd Ctrl K >}}里的Ctrl不是字符串参数。 - 不用它标命令:
hugo server写成行内代码,Ctrl是按键。
相关
20 - 引用
三个 shortcode 各做一件事:include 把另一个文件的内容放进当前页面,param 打印一个页面或站点参数,comment 丢弃一段内容。适用于跨页复用的片段与散落在多页的常量:同一段安装步骤出现在三页时用 include,版本号出现在几十页时用 param,改一处即可。只在一页出现的内容写在那一页。
最简例子
include 只有一个必填参数 file:
被引的文件是一段普通 Markdown,放在 assets/ 下:
渲染结果与写在本页里相同:代码块有复制按钮,提示块是提示块。
把 OINK 安装到一个已有的 Hugo 站点,三条命令:
hugo mod get 需要本机安装 Go;用离线归档或 submodule 时不需要。
当前发布版本是 v0.6.0。
被引的文件不是一篇独立页面:它不出现在侧栏、不参与翻译配对、没有自己的 URL。
文件位置
file 按下面的顺序解析,第一个命中的胜出:
| 顺序 | 找哪里 | 写法 |
|---|---|---|
| 1 | 当前页面的页面资源(页面包里的文件) | file="config.yaml" |
| 2 | 全局资源 assets/ 下的文件 | file="snippets/dsn.txt" |
| 3 | content/ 下的文件:/ 开头是内容根目录,否则相对当前页面所在目录 | file="notes/caveat.md"、file="/shared/notice.md" |
三处都找不到时构建失败,不输出占位内容。路径里含 .. 也让构建失败:引用只能在 content/ 与 assets/ 中取文件。
引 Markdown 片段时写文件在磁盘上的真名。有一个陷阱只属于第 1 步:Hugo 把带语言后缀的页面资源(如 notice.zh.md)按去掉后缀的名字挂在页面上,向页面包索取 notice.md 拿到的是已渲染的 HTML 而不是源码,Markdown 输出里会出现 <div class="td-code">。assets/ 与 content/ 下写什么名字就取什么文件,没有这层转换。非 Markdown 文件(.yaml、.sh、.txt)也没有这个区别。
本页两种语言各引一份自己的片段:中文引 assets/parts/install-oink.zh.md,英文引 assets/parts/install-oink.md。片段放在 assets/ 下而不是页面包里,两种语言就都按写下的名字取到源码。
引入代码文件
加 code=true 让文件按代码块渲染,lang= 指定高亮语言。引用仓库里的真实配置文件,文档与实际文件不会不一致。
代码块与围栏走同一条渲染管线:高亮、行号、复制按钮都有。围栏属性(title=、collapse、hl_lines=)传不进来,需要它们时把文件内容写成普通代码块。
片段内容
片段是页面级 Markdown,在当前页面的上下文里渲染:提示块、表格、列表、图片、步骤与 shortcode 都可以用。上面那段片段结尾的「当前发布版本是 v0.6.0」,是片段里的 {{< param version >}} 在本页展开的结果。
一个片段被两页引用时,两页各自渲染一遍,各自生成标题锚点与代码块 ID,互不冲突。
安装命令、连接串、支持矩阵、法务声明:会变动、且变动时必须处处同步的内容。只在一页出现的内容写在那一页。
插入站点参数
param 打印一个参数:先查本页 front matter,查不到再查站点配置(Hugo 的 .Param 规则)。
本站发布版本 v0.6.0,版权起始年 2026,
本页 front matter 里写了 pigsty_pg_major: 18,这里取到 18。
嵌套键用 . 连接,copyright.from_year 取的是 params.copyright.from_year。参数不存在、或者值是 map 与列表而不是标量时构建失败,不会留下空白。
在命令、表格与链接里插参数
param 的输出是转义后的纯文本,可以放进代码围栏、表格单元格与链接地址。安装命令里的版本号适合这么写:
| 项目 | 值 |
|---|---|
| 当前版本 | v0.6.0 |
| Hugo 下限 | 0.160.1 |
站点参数在哪里定义、有哪些可用,见配置总览;页面参数见页面参数。
构建期删除的注释
comment 的内容在 HTML、打印、Markdown、RSS 四种输出里都不出现。HTML 注释不同:它留在页面源码里,也会进入 llms.txt。
PostgreSQL 18 起 pg_stat_io 拆分了 WAL 统计。
升级前先在测试库上验证监控面板。
上面两段之间有一段注释,查看页面源码也找不到它。
输出形态
| 输出 | include(Markdown) | include code=true | param | comment |
|---|---|---|---|---|
| HTML | 片段渲染成正常内容 | 高亮代码块 + 复制按钮 | 转义后的纯文本 | 无 |
| 打印 | 同 HTML | 同 HTML,无复制按钮 | 同 HTML | 无 |
| Markdown | 片段的源码原样输出 | 源码围栏 | 值本身 | 无 |
| RSS | 同 HTML | 同 HTML | 同 HTML | 无 |
Markdown 输出里片段是源码而不是 HTML,片段里的 shortcode 保持 {{< param version >}} 的原样。这与「Markdown 输出保留源码」一致,不是漏渲染。三个 shortcode 都不加载脚本。
参数参考
include(只接受具名参数):
file, ,- 解析顺序见文件放在哪;含
..、文件缺失、空值都构建失败 code, ,true时按代码块渲染;必须写成code=true,带引号的code="true"是字符串,构建失败lang, ,- 代码语言;只能与
code=true同用,单独出现构建失败
其它任何参数名都会构建失败,报错里带文件名与行号。
param(一个位置参数):
参数名, ,- 嵌套键用
.连接;先页面 front matter 后站点params;缺失或非标量(map / 列表)构建失败
comment 没有参数,成对使用,{{< comment >}} 与 {{< /comment >}} 之间的内容整段丢弃。
限制与常见问题
include不是模板:不能向片段传变量、不能条件引入、不能给引入的代码块加围栏属性(title=、collapse)。按平台分版本时写两个片段配标签页。- 片段的语言要自己维护:
include不做语言回退。中文页引中文片段,英文页引英文片段,两份文件并列存放(install-oink.zh.md与install-oink.md)。 param只打印标量:结构化数据(版本矩阵、下载列表)用data/目录里的数据配对应组件渲染。comment不是「暂时不发布」:内容每次构建都被丢弃,临时下线整页用draft: true。- 不把
include当目录页:一页引入十个片段时,读者需要的是十条链接。
相关
21 - Asciinema
asciinema 把一段 .cast 录像渲染成页面里的终端播放器。适用于命令行流程的演示:终端里的文字仍然是文字,可以选中复制,一段六分多钟的安装过程约 190 KB。图形界面的操作用截图或视频,本组件只播放终端录像。播放器与样式随主题分发,构建期不下载、运行期不连 CDN,只有用到它的页面加载这套运行时。
最简例子
只有 file 是必填的:
这段录像是 Pigsty 在一台 Debian 机器上的单机安装,120×36 的终端,约 6 分 40 秒。文件在本站的 static/images/install.cast,路径写站点根路径。放在 assets/ 下也写相对路径:主题先在资源里查找,找不到再当成站点根路径。不写 title 时,窗口标题显示 file 的值。
窗口标题与主题
title 设置窗口标题,theme 设置配色:
theme 默认 auto:跟随站点的深浅色,浅色用 td-light,深色用 td-dark,读者切换配色时播放器就地重挂一次。要固定成某套终端配色时,可选值是播放器自带的 asciinema、dracula、gruvbox-dark、monokai、nord、seti、solarized-dark、solarized-light、tango,以及主题提供的 td-light / td-dark。固定的主题不跟随深浅色,深色站点配 solarized-light 的对比度不合适。终端字体不用单独设置:播放器使用站点的代码字体,与页面上的代码块一致。
速度、起点与封面
长录像用三个参数控制起点:speed 设倍速,startAt 跳过开头,poster 决定未播放时定格的画面。
speed 与 startAt 是数字(秒),poster 用播放器的 npt: 记法定位时间点,npt:1:30 是第 1 分 30 秒。上面这个播放器停在第 90 秒的画面,点播放从第 60 秒开始。
idleTimeLimit 把静默段压缩到最多 N 秒。这段录像在录制时已经压缩过(.cast 头里是 idle_time_limit: 0.5),此处不必再设。只有录制时没有限制静默时长的文件才需要它。
尺寸与适配
播放器默认按容器宽度缩放(fit="width"),终端的行列数来自 .cast 文件头。cols / rows 可以覆盖它:
比录像本身小的行列数会裁掉内容,上面这个只显示 36 行里的 16 行。cols / rows 用于修正录像头里的错误尺寸,不是排版工具。要让播放器变矮,重录一次小终端。
fit 的四个值:width(默认,按宽度缩放)、height(按高度)、both(两个方向都装下)、none(不缩放,按字号原样显示,宽终端会溢出)。
循环与预加载
loop 播完自动重播,preload 在页面加载时取回 .cast,点播放不必等待:
autoplay="true" 让页面打开即播。不建议使用:系统的「减少动态效果」偏好只关闭播放器控件的过渡动画,不阻止自动播放。确实需要自动播放时,配上 loop、很短的内容,并且一页只放一个。
放进步骤里
录像放在某一步旁边:文字说明要做什么,录像展示实际输出。
安装依赖,获取安装脚本:
执行安装,全程约六分钟:
pig install打开
http://<节点地址>:3000,用admin / pigsty登录 Grafana。
一页可以放多个播放器,脚本与样式只加载一次。
录制 cast 文件
主题只负责播放。用 asciinema 的 asciinema rec --idle-time-limit=2 --cols=100 --rows=28 install.cast 录制,asciinema play install.cast 本地回放确认。
- 终端宽度控制在 100 列以内,窄屏上仍可读;录制前先
clear。 - 录制前清理密钥:
.cast是纯文本,录像里的每个字符都能grep到,提交前检查一遍。 - 文件放进
static/images/或页面包并提交进仓库,不引用外站的.castURL。
输出形态
| 输出 | 呈现 |
|---|---|
| HTML | <div class="td-asciinema"> 窗口外框 + 播放器;播放器 CSS/JS 与初始化脚本按需加载,一页一次 |
| 打印 | 同 HTML(打印输出也加载播放器);印到纸上只会留下当时的一帧 |
| Markdown | 同一段容器 HTML 加一个 JSON 配置块;纯文字里能读到的只有窗口标题 |
| RSS | 同一段静态标记;阅读器不执行脚本,只剩一个空窗口外框 |
录像不能是唯一的信息来源。关键命令与关键输出要在录像旁边用文字或代码块写一遍:离线读者、llms.txt 的抓取方与打印读者只能看到这些文字。
参数参考
file, ,- 具名或第一个位置参数;先按全局资源找,找不到当站点根路径;带协议的完整 URL 原样透传
title, ,- 窗口标题
theme, ,auto跟随站点深浅色;或td-lighttd-darkasciinemadraculagruvbox-darkmonokainordsetisolarized-darksolarized-lighttangofit, ,widthheightbothnone;其它值构建失败cols/rows, ,- 覆盖终端行列数;比录像小会裁掉内容
speed, ,- 播放倍速
startAt, ,- 起播位置
idleTimeLimit, ,- 静默段最多播这么久
poster, ,- 未播放时定格的画面,
npt:分:秒 autoplay, ,- 页面加载即播;不建议
loop, ,- 循环播放
preload, ,- 页面加载时就取回
.cast pauseOnMarkers, ,- 播到章节标记处暂停
markers, ,- 章节标记;见下面的限制,标签目前到不了播放器
布尔类参数比较的是文本 true:loop="true" 与 loop=true 都表示开启,其它值表示关闭。fit 的取值由主题校验,非法值报错并给出参数名。数字类参数(speed cols rows startAt idleTimeLimit)写成非数字会在转换时让构建失败。
限制与常见问题
markers的标签会丢失:主题把时间:标签的列表拼成一维数组交给播放器,播放器只接受成对写法,时间轴上会多出没有标签的标记点。需要章节时用录像旁边的文字列表。- 播放器需要 JavaScript:禁用脚本时,以及 Markdown 与 RSS 输出里只有一个空窗口,见输出形态。
- 录像不进搜索:站内搜索索引页面文字,录像里出现过的命令搜不到。
- 不引用远程
.cast:file收到带协议的 URL 会原样透传给播放器,页面因此依赖一个外站。 - 控制单段长度:超过五六分钟的录像少有人看完,长流程拆成几段短录像,各配一段文字。