这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

内容与自定义

如何为 Docsy 站点添加内容并进行自定义。

1 - Logo 与图片

在项目中添加和自定义 Logo、图标与图片。

默认情况下,OINK 会在顶部导航栏起始位置(即最左侧)显示站点 Logo。把项目的 SVG Logo 放在 assets/icons/logo.svg,即可覆盖主题中的默认 Logo。

如果不希望顶部导航栏显示 Logo,请在项目配置中把站点参数 navbar_logo 设为 false

[params.ui]
navbar_logo = false
params:
  ui:
    navbar_logo: false
{
  "params": {
    "ui": {
      "navbar_logo": false
    }
  }
}

Logo 样式的更多信息请参阅设置项目 Logo 与名称的样式

使用图标

OINK 默认包含免费版 Font Awesome 图标,其中也包括 GitHub、Stack Overflow 等站点的 Logo。可以在 Font Awesome 文档中查看全部可用图标、每个图标加入的 Font Awesome 版本,以及它是否对免费版用户开放。OINK 随发行物内置已经固定版本的字体与图标;确切版本记录在 theme/VENDOR.json 和发布说明中。

你可以把 Font Awesome 图标添加到顶部导航栏侧栏导航或正文中的任意位置。

添加 favicon

主题本身不提供 favicon 文件,但会 发现并链接 采用约定名称的图标。请生成 favicon 文件,然后放入项目的 static 目录,使其发布到站点根目录——浏览器会在那里探测这些文件。OINK 会按以下顺序,为找到的文件在每个页面的 <head> 中添加 <link> 元素:

文件 链接
favicon.ico rel="icon"1
favicon.svg rel="icon",并带有 type="image/svg+xml"
favicon-NxN.png rel="icon",并带有 type="image/png" sizes="NxN"
apple-touch-icon.png rel="apple-touch-icon"(隐含尺寸为 180×180)
apple-touch-icon-NxN.png rel="apple-touch-icon",并带有 sizes="NxN"

如果提供了上述任意方形尺寸变体,OINK 会按尺寸升序添加。

一个现代 favicon.ico 加上 SVG 和 apple-touch-icon.png,足以覆盖常见浏览器与平台的 favicon 需求。如需更多能力:

生成 favicon

还没有 favicon?可以通过 favicon.ioRealFaviconGenerator 等在线工具,从单张图片生成 favicon。

如果已经有源 SVG 并安装了 ImageMagick,OINK 也保留 gen-favicons 辅助工具。把源 SVG 保存为 static/favicon.svg——主题会直接链接它——再在同一位置生成栅格图标。从站点项目根目录运行命令。

对于上游 Docsy npm 包安装:

npx --no-install gen-favicons static/favicon.svg static/

其他安装方式运行:

node OINK_THEME_DIR/scripts/gen-favicons/cli.mjs static/favicon.svg static/

OINK_THEME_DIR 替换为实际主题目录。使用 Git submodule 时通常是 themes/oink/theme;本仓库中则是 theme/。运行带 --help 的命令可以查看尺寸与其他选项。

该辅助工具只用于一次性生成素材,并不是站点构建依赖。消费端生产构建仍然只运行 Hugo;也可以使用其他获准的图片工具生成同名文件。

添加图片

落地页

OINK 的 blocks/cover 短代码可以方便地为落地页添加封面图(也称为 Hero 图片)。短代码会在落地页的页面包中查找文件名包含 background 的图片。

例如,示例站点的落地页 content/en/_index.md 使用同一目录下的图片 content/en/featured-background.jpg;可在 GitHub 上查看 content/en 文件夹。

通过区块的 height 参数设置封面容器及其图片的首选显示高度。要铺满视口高度,请使用 full,并配合 td-below-navbar 辅助类把封面放在顶部导航栏下方:

{{% blocks/cover
  title="Welcome to OINK!"
  image_anchor="top"
  height="full td-below-navbar"
%}}
...
{{% /blocks/cover %}}

要使用较矮的图片,可以选择 minmedmax,或表示图片自然高度的 auto

{{% blocks/cover
  title="About the OINK Example"
  image_anchor="bottom"
  height="min td-below-navbar"
%}}
...
{{% /blocks/cover %}}

其他页面

要在其他页面中添加行内图片,可以使用 imgproc 短代码。也可以直接使用普通 Markdown 或 HTML 图片,并将图片文件放入项目的 static 目录。该目录的更多信息请参阅添加静态内容


  1. .ico 链接不声明 sizes:文件本身会描述所含帧尺寸(浏览器会读取),在链接中声明尺寸只会带来与真实文件不一致的风险。同时提供 favicon.svg 时,支持 SVG favicon 的浏览器(绝大多数现代浏览器)会优先使用它,.ico 则作为回退。 ↩︎

2 - 代码仓库链接与页面信息

帮助读者查看、编辑页面源码,并针对源码报告问题。

OINK 的文档与博客布局可以显示指向当前页面源码仓库的链接:

  • 查看页面源码:打开源文件。
  • 编辑本页:打开可编辑的源码视图。
  • 创建子页面:在当前页面下新建文件,并可使用站点的 assets/stubs/new-page-template.md 模板。
  • 创建文档 issue:携带页面上下文,在文档仓库中创建 issue。
  • 创建项目 issue:可选地把 issue 提交到另一个产品仓库。

内置 URL 模式面向 GitHub 风格的代码仓库。如果使用其他兼容托管服务,请逐项验证;如果 URL 结构不同,应覆盖相应 partial。

典型站点配置如下:

params:
  github_repo: https://github.com/OWNER/DOCS
  github_project_repo: https://github.com/OWNER/PRODUCT
  github_branch: main
  github_subdir: site

当内容来自多个代码仓库时,可以在全局、单种语言、分区 cascade 或页面 front matter 中设置这些值。

github_repo

文档源码仓库 URL。它用于生成查看、编辑、创建子页面和创建文档 issue 链接:

params:
  github_repo: https://github.com/pgsty/oink

省略后将隐藏从仓库派生的页面操作。如果页面源码实际位于消费站点,不要把它错误地指向主题仓库。

github_subdir(可选)

设置从仓库根目录到 Hugo 站点源码的路径。本项目把站点存放在 oink.pgsty.com 中:

params:
  github_subdir: oink.pgsty.com

该值是仓库内路径,不是本地绝对路径;除非内容目录就是实际站点根目录,否则也不能直接填写内容目录。

github_project_repo(可选)

设置另一个产品仓库,以显示 创建项目 issue

params:
  github_project_repo: https://github.com/OWNER/PRODUCT

内容缺陷应提交到文档仓库,页面讨论的产品行为应提交到产品仓库。如果读者无法清楚理解两者区别,应省略第二条链接。

github_branch(可选)

设置源码与编辑 URL 使用的分支:

params:
  github_branch: main

通常应填写站点源码分支。它不一定是部署分支、自动生成的 Pages 分支或主题修订版本。

path_base_for_github_subdir(可选)

如果某棵内容子树从另一个仓库挂载,请使用分区 cascade。系统会先移除 path base,再把剩余内容路径附加到 github_subdir

---
title: Imported reference
cascade:
  github_repo: https://github.com/OWNER/UPSTREAM
  github_project_repo: https://github.com/OWNER/UPSTREAM
  github_subdir: docs
  path_base_for_github_subdir: content/reference
---

对于源页面 content/reference/api/client.md,以上配置会把仓库路径映射为 docs/api/client.md

path_base_for_github_subdir 可以是正则表达式。按语言目录组织内容的站点可以写成:

path_base_for_github_subdir: content/\w+/reference

OINK 将 .md.zh.md 并置保存,通常两种语言使用相同静态 base,因此表达式中不需要语言目录。

如果源文件使用不同名称,请使用 fromto 映射。下面把分区 _index.md 映射到上游 README.md

path_base_for_github_subdir:
  from: content/reference/(.*?)/_index.md
  to: $1/README.md

请分别从叶子页、分区页和两种语言页面测试查看与编辑链接。正则表达式移除路径过多时,可能生成看似合理却指向错误位置的仓库 URL。

github_url(可选)

旧页面可以在 front matter 中设置完整的自定义编辑 URL:

---
title: Imported page
github_url: https://github.com/OWNER/UPSTREAM/edit/main/README.md
---

使用该值的页面只显示 编辑本页。当目标与 GitHub 不兼容时,更适合使用站点专属模板覆盖。

每种操作都有稳定的 CSS 类:

链接 CSS 类
查看页面源码 .td-page-meta__view
编辑本页 .td-page-meta__edit
创建子页面 .td-page-meta__child
创建文档 issue .td-page-meta__issue
创建项目 issue .td-page-meta__project-issue

当目标不支持某项操作时,可以在 assets/scss/_styles_project.scss 中将其隐藏:

.td-page-meta__child {
  display: none;
}

对于全局不可用的目标,应优先从配置中省略。CSS 隐藏适合选择性策略,但不能让错误链接变正确。

页面最后修改信息

启用 Hugo Git 信息并配置源码仓库:

enableGitInfo: true
params:
  github_repo: https://github.com/OWNER/DOCS

OINK 随后可以在文档与博客页显示最后一次提交的日期、主题、hash 和源码链接。CI 必须为当前文件获取足够的 Git 历史;浅克隆可能导致元数据缺失或产生误导。

如果要在特定站点或分区隐藏提示,可以覆盖样式或负责页面元信息的 partial。当 Git 历史不可用时,不要把构建时间冒充为“最后修改”时间。

3 - 打印支持

让整节文档更便于打印。

大多数浏览器都能很好地打印单篇文档,因为页面样式会从打印输出中移除导航外壳。

有些站点适合启用“打印整节”功能(本用户指南就是如此)。选择后,系统会把当前顶层分区(本页所在的“内容与自定义”等)连同全部子页面和子分区渲染为适合打印的格式,并附上该分区的完整目录。

要启用此功能,请在站点的 hugo.tomlhugo.yamlhugo.json 中,为 section 类型添加 print 输出格式:

[outputs]
section = [ "HTML", "RSS", "print" ]
outputs:
  section:
    - HTML
    - RSS
    - print
{
  "outputs": {
    "section": [
      "HTML",
      "RSS",
      "print"
    ]
  }
}

随后,站点右侧导航中会显示“打印整节”链接。

进一步自定义

禁用目录

如果不希望可打印视图显示目录,可以在页面 front matter,或者 hugo.tomlhugo.yamlhugo.json 中将 disable_toc 参数设为 true

+++

disable_toc = true

+++
---

disable_toc: true

---
{
  …,
  "disable_toc": true,
  
}
[params.print]
disable_toc = true
params:
  print:
    disable_toc: true
{
  "params": {
    "print": {
      "disable_toc": true
    }
  }
}

布局钩子

主题定义了多种布局 partial 和钩子,可用来定制打印格式。这些文件位于 layouts/_partials/print

钩子可以按内容类型定义。例如,如果希望 blog 页与 docs 页使用不同的标题布局,可以创建 layouts/_partials/print/page-heading-<type>.html,例如 page-heading-blog.html。默认实现使用页面标题和描述作为页首标题。

同理,可以通过创建 layouts/_partials/print/content-<type>.html 来定制每个页面的正文格式。

4 - 导航与菜单

配置 OINK 导航、语言切换、侧边栏和页面大纲。

OINK 把 Hugo 的内容树和菜单模型组织成一套文档工作台:全局导航栏、可折叠且可调整宽度的分区侧边栏,以及可折叠的页面大纲。同一套结构适用于英文、中文和从右向左书写的语言。

全局导航栏由 Hugo 的 main 菜单与 OINK 自动生成的控件组成。根据配置和页面类型,其中可以显示版本、语言、颜色模式与搜索控件。

添加 main 菜单项

可以在页面 Front Matter 中定义菜单项:

---
title: 文档
linkTitle: 文档
menu:
  main:
    weight: 20
    pre: <i class="fa-solid fa-book" aria-hidden="true"></i>
---

权重越小,位置越靠前。站点级外部链接写法类似:

menus:
  main:
    - name: GitHub
      identifier: github
      weight: 50
      url: https://github.com/pgsty/oink
      pre: <i class="fa-brands fa-github" aria-hidden="true"></i>

需要在配置中引用菜单项时,应为其设置 identifiernamelinkTitle 可以按语言翻译,但标识符必须稳定。

版本菜单

配置 params.versions 后会显示版本选择器。条目可以表示标题、分隔线、正式版本、开发版本或站点变体:

params:
  version: v1.0.0
  version_menu: v1.0.0
  version_menu_pagelinks: true
  versions:
    - version: v1.1.0-dev
      kind: next
      url: https://next.example.org/
    - version: v1.0.0
      kind: latest
      url: https://docs.example.org/

version 标识已发布的站点变体,不一定是 Git 引用。安装命令等必须使用可解析标签的内容,应改用项目显式定义的发布引用参数。启用页面链接后,OINK 会先尝试目标版本中的同一路径,找不到时再使用条目配置的 URL。

语言菜单

OINK 根据 Hugo 的 AllTranslations 构造语言目标。当前页面缺少某种语言译文时,会链接到该语言首页,而不是生成损坏的 URL。只配置一种语言时不显示控件;配置两种或更多语言时,直接点击会按 weight 顺序切换到下一种语言,悬停半秒或聚焦控件则打开完整菜单。当前站点按英文、简体中文的顺序循环。目标链接包含 langhreflang、locale 与文字方向属性。

浅色/深色主题菜单

启用颜色模式后,导航栏与文档工作台会显示主题控件。详见浅色/深色模式菜单

启用离线搜索后,文档工作台会使用本地搜索对话框。侧边栏按钮会显示当前平台快捷键(Command/Ctrl+K)。在线搜索集成仍可通过显式配置启用。详见搜索

为导航栏添加图标

在菜单项中使用 prepost。OINK 已在本地提供免费版 Font Awesome 资源:

menus:
  main:
    - name: 源码
      identifier: source
      url: https://github.com/pgsty/oink
      weight: 50
      pre: <i class="fa-brands fa-github" aria-hidden="true"></i>
      post: <span class="visually-hidden">(外部链接)</span>

装饰性图标需要设置 aria-hidden="true";链接本身必须保留有意义的文字或无障碍标签。在新标签页打开的外部链接必须使用 rel="noopener"

侧边导航

文档页与博客页的左侧面板由内容层级自动生成。OINK 按 weight 排序,并在存在 linkTitle 时用它作为标签。分区来自 _index.md;翻译后的分区需要配套 _index.zh.md,才能正确本地化导航元数据。

从侧边栏隐藏页面:

toc_hide: true

从分区落地页摘要中隐藏页面则使用 hide_summary: true。只有页面确实不应出现在这两个发现入口中时,才同时设置二者。

侧边导航选项

常用控制项如下:

params:
  ui:
    sidebar_menu_compact: true
    sidebar_menu_foldable: true
    sidebar_menu_truncate: 128
    sidebar_cache_limit: 2000
    sidebar_search_disable: false
    sidebar_width_min: 220
    sidebar_width_max: 480
    sidebar_item_overflow: ellipsis
  • sidebar_menu_compact 只显示当前分支和附近条目;
  • sidebar_menu_foldable 允许读者展开或折叠分区;
  • sidebar_menu_truncate 限制条目数,数值过小时会发出构建警告;
  • sidebar_cache_limit 在站点规模超过阈值后启用共享导航标记;
  • sidebar_width_minsidebar_width_max 限制桌面端拖拽调整的宽度;
  • sidebar_item_overflow 默认为 ellipsis,长标签需要换行时改用 wrap

折叠状态、宽度和滚动位置保存在读者本地。移动端会转换为带遮罩层和安全焦点控件的可关闭抽屉。

为侧边导航添加图标

在页面 Front Matter 中设置 icon

---
title: 运维
icon: fa-solid fa-screwdriver-wrench
---

同级条目的图标用法应保持一致。图标只是辅助线索,不能取代文字标签。

在所需位置创建占位页面:

---
title: API 状态
weight: 90
manualLink: https://status.example.org/
manualLinkTitle: 实时服务状态
manualLinkTarget: _blank
---

内部内容引用应使用 manualLinkRelref 而不是 manualLink;Hugo 无法解析目标时会令构建失败。OINK 会为新标签页链接补充 noopener。由于 Hugo 仍会为占位文件生成页面,正文应简短说明实际去向。

启用根侧边栏:

params:
  ui:
    sidebar_root_enabled: true
    sidebar_root_menu: true

然后在分区的 _index.md 中设置:

---
title: API Reference v2
sidebar_root_for: self
sidebar_root_link_self: true
---

self 会把该根节点应用于分区索引及其后代;children 会把索引留在父级树中,只限制其后代。可选的根菜单用于在不同根节点之间切换。根分区可以嵌套,但冗余或无效取值会触发构建警告。

页面目录

Hugo 根据 Markdown 标题生成右侧页面大纲。OINK 将其渲染为固定文档面板,并放置快捷链接、语言与主题控件、仓库元数据和分类标签。读者可以折叠该面板,状态保存在本地。

由 Markdown 短代码({{% ... %}})输出的标题会进入 Hugo 目录;仅由标准短代码({{< ... >}})输出的标题通常不会进入。因此,只要条件允许,内容结构都应保留在 Markdown 中。

目录定制

在单个页面隐藏大纲:

notoc: true

配置 Hugo 收录的标题层级:

markup:
  tableOfContents:
    startLevel: 2
    endLevel: 4
    ordered: false

toc_on_this_page 等标签在站点 i18n 资源包中翻译。自定义 CSS 调整大纲轨道或固定面板尺寸后,需要测试活动项跟踪、缩放、键盘焦点,以及完全没有标题的页面。

使用 ScrollSpy 跟踪目录活动项

OINK 使用本地 Bootstrap ScrollSpy 补丁与 IntersectionObserver 跟踪活动标题。工作台会绘制连续轨道、活动区段和位置标记。为某个页面关闭跟踪:

params:
  ui:
    scrollSpy:
      disable: true

旧版 ScrollSpy 配置也接受全局 rootMargin。它会改变条目进入活动状态的时机,应在短分区、长分区和直接片段导航中分别测试。

ScrollSpy 高级定制

优先使用配置与项目 CSS。覆盖 ScrollSpy 属性 Partial 或 docs-shell.js 会形成实现级分支;必须增加浏览器 Fixture,覆盖哈希更新、前进/后退导航、尺寸变化、减少动态效果模式,以及存在重复或缺失 ID 的页面。

普通内容页上方和分类结果中会显示面包屑。全局关闭方式如下:

params:
  ui:
    breadcrumb_disable: true
    taxonomy_breadcrumb_disable: true

页面或分区 cascade 也可以设置 ui.breadcrumb_disable。面包屑标签来自本地化页面标题,而且必须与侧边栏遵循同一逻辑层级。

使用方站点可以启用 OINK 标题渲染钩子:

{{ partial "td/render-heading.html" . }}

生成的 .td-heading-self-link 控件默认使用 #。它在触控设备上始终可见,在指针设备上则于悬停或聚焦时出现。链接必须支持键盘访问,并保留足以避开固定导航的滚动偏移。

标题别名与页内目标

修改标题可能破坏外部片段链接,因此标题 ID 应按公开路由对待。需要重命名 ID 时,应保留旧 ID 的空锚点,并显式写入新 ID:

## Quickstart <a id="get-started"></a> {#quickstart}

别名和其他页内目标应使用空的 <a id="..."></a>。不要仅为片段目标使用 span。ID 必须唯一、稳定,在可行时使用 ASCII,并在各语言版本中保持一致。

快速开始

这个真实标题演示了 #get-started#quickstart 都能到达同一位置。译文标题应显式写入英文页面渲染后的 ID,不要依赖不同语言各自生成的自动 slug。

实现说明

  • 文档为固定界面设置全局滚动偏移;
  • 内置块目标使用 td-anchor-no-extra-offset,避免重复应用额外偏移;
  • 翻译审计会比较英文与中文页面渲染后的标题 ID;
  • 删除旧别名属于破坏性文档变更,需要重定向或明确记录兼容性决策。

5 - 短代码

安全、无障碍地使用 OINK 的本地优先内容组件。

短代码用于表达普通 Markdown 无法承载的行为。OINK 保留 Docsy 核心组件,并新增本地提供的图表、终端录像、信息图、轮播、卡片和折叠组件。浏览器运行时只在实际使用它们的页面加载。

标题、正文、列表、链接、表格和图片应优先使用 Markdown。短代码一旦投入使用,就成为内容 API 的一部分:修改名称或参数可能破坏所有调用它的页面。

短代码分隔符

Hugo 支持两种形式:

  • {{< name >}} 使用标准分隔符,原样传递内部内容;
  • {{% name %}} 使用 Markdown 分隔符,在周围内容的上下文中渲染内部 Markdown。

请采用各组件文档指定的形式。嵌套、缩进和空行都会影响结果,在列表和块引用中尤其如此。示例里的 /* ... */ 转义用于防止 Hugo 执行正在展示的短代码。

blocks/* 短代码

块短代码用于组合全宽落地页。color 参数使用 OINK/Bootstrap 语义颜色或项目自定义块样式,height 参数接受各组件说明的取值。

blocks/cover

使用页面包中匹配 *background* 的图片以及可选的 *logo* 创建首屏:

{{< blocks/cover title="OINK" subtitle="本地优先文档"
    color="dark" height="max" >}} [开始使用](/zh/docs/get-started/){ .btn
.btn-lg .btn-primary } {{< /blocks/cover >}}

image_anchorlogo_anchor 控制图片裁切位置,byline 用于标注图片来源。高度可取 autominmedmaxfull。即使背景无法显示,首屏关键信息也必须保持可读。

blocks/lead

创建醒目的介绍区块:

{{% blocks/lead color="primary" height="min" %}} OINK 只用 Hugo
Extended 即可构建完整文档体验。 {{% /blocks/lead %}}

高度支持 autominmedmaxfull

blocks/section

创建通用落地页区块:

{{% blocks/section color="light" type="row" height="auto" %}}

### 一个分区

区块内部使用普通 Markdown。 {{% /blocks/section %}}

type 选择容器形式,height 使用块高度取值。标题级别必须与页面大纲保持一致。

blocks/feature

创建单个功能单元,通常放在 Section 中:

{{% blocks/feature icon="fa-solid fa-box-archive"
    title="离线可用" url="/zh/docs/oink/local-first/"
    url_text="阅读设计说明" %}} 所需浏览器资源均已锁定版本并从本地提供。
{{% /blocks/feature %}}

图标只是装饰,含义必须由 title 和链接文本表达。

从当前块添加指向下一块的链接。它必须嵌套在块内。生成目标必须长期稳定时,应显式设置 id

导航栏下方布局校正

直接位于固定导航下方的块使用 td-below-navbar/td-anchor-no-extra-offset 校正导航栏高度。不要自行添加任意上边距;修改导航栏尺寸后,应验证直接访问片段链接的效果。

辅助短代码

alert

旧版告警短代码仍可使用:

{{% alert title="兼容性说明" color="warning" %}}
新内容优先使用 Markdown 块引用告警。 {{% /alert %}}

color 映射到 Bootstrap 告警后缀。新内容通常应采用添加内容介绍的 Markdown 告警语法。

告警、缩进与示例

开始和结束短代码应与外层列表或块引用对齐,块级 Markdown 前后应保留空行。需要原样展示短代码时,应转义分隔符,不要把活动调用包在另一个组件中。

pageinfo

在 Markdown 外渲染信息面板:

{{% pageinfo color="info" %}} 本页介绍预览接口。 {{% /pageinfo %}}

警告信息应使用语义告警;pageinfo 适合提供页面上下文。

imgproc

处理当前页面包中的图片:

{{% imgproc "architecture" Fit "960x540" %}} OINK 运行时架构。
{{% /imgproc %}}

命令可取 FitResizeFillCrop,第三个参数遵循 Hugo 图片处理语法。内部文字会成为图注;资源存在 params.byline 时会附加署名。始终提供有意义的替代文字或相邻说明。

swaggerui

嵌入本地纳管的 Swagger UI 运行时:

{{< swaggerui src="/openapi.yaml" >}}

离线或严格 CSP 部署应使用同源规范。远程 src 是显式网络依赖,也可能向该主机暴露读者元数据。当前兼容短代码在一页中只应放置一个 Swagger UI 实例。

redoc

嵌入本地纳管的 Redoc 运行时:

{{< redoc "openapi.yaml" >}}

第一个参数可以是页面相对、站点相对或显式 HTTP 规范;可选第二个参数包含 Redoc 元素选项。规范内容必须经过审查,大型 Schema 还应测试移动端表现。

iframe

嵌入另一个页面:

{{< iframe src="/demo/" name="demo" id="demo-frame"
    sandbox="allow-scripts allow-same-origin" >}}

请设置有描述力的 name、唯一的 id、后备 sub 提示,以及满足需求的最严格 sandbox。默认值支持宽度和自动高度,但跨域文档并不总能测量。iframe 是安全与隐私边界,不是通用布局工具。

OINK 内容组件

以下组件由 OINK 新增。各运行时都在 theme/VENDOR.json 中锁定版本,并按需从同源加载。

details

创建无障碍折叠内容:

{{% details title="显示迁移说明" closed="false" %}} 正文支持 Markdown。
{{% /details %}}

closed 默认为 true。摘要应简洁,而且不得把强制操作隐藏在默认关闭的折叠区中。

asciinema

播放 asciinema .cast 录像:

{{< asciinema file="casts/install.cast" speed="1.25"
    markers="0:开始,18:验证" fit="width" >}}

主要参数包括 themeautoplaylooppreloadspeedstartAtpostercolsrowsidleTimeLimitpauseOnMarkersmarkersfitwidthheightbothnone)。本地录像可以来自 Hugo assets 或站点相对 URL。不要自动播放,必须清除终端历史中的机密,并为关键步骤提供相邻文字说明。

echarts

根据 JSON 或 YAML 选项对象渲染 Apache ECharts:

{{< echarts height="320px" >}} xAxis: type: category data:
[构建, 测试, 发布] yAxis: type: value series:

- type: bar data: [42, 38, 12] {{< /echarts >}}

height 必须是安全的 CSS 长度;theme 选择 ECharts 主题,full=true 会取消常规正文宽度限制。

短代码内部的 JavaScript 块默认会被拒绝。只有单次调用设置 unsafe=true,或全局设置 params.content.echarts_unsafe=true 时才能执行。该选项允许可执行内容,绝不能为不可信作者启用。应优先使用声明式 JSON/YAML,添加相邻文字摘要,并验证深色模式。

infographic

渲染本地纳管的信息图 DSL:

{{< infographic height="360px" >}} infographic
list-row-simple-horizontal-arrow data items - label 构建 - label 测试 -
label 发布 {{< /infographic >}}

height 可以是 auto 或安全 CSS 长度;full=true 会取消宽度限制。DSL 属于数据,并非任意 HTML。可视化不可用时,相邻正文也必须能表达相同结论。

doc-cardsnav-cards

两个容器都接受 1 至 4 的 cols。子卡片接受 titlelinkimagealticondescaccentbadge

{{< nav-cards cols="2" >}}
{{< nav-card title="开始使用" link="/zh/docs/get-started/"
      icon="fa-solid fa-rocket" desc="使用 Hugo {version} 构建。" >}} {{< nav-card title="架构" link="/zh/docs/oink/architecture/"
      badge="设计" >}}
{{< /nav-cards >}}

doc-card/doc-cards 与其共享渲染契约,适合编辑型内容;nav-card/nav-cards 则明确表示导航。{version} 等描述占位符会从站点参数解析。卡片图片采用延迟加载;除非图片纯属装饰,否则必须提供有意义的 alt

doc-card 放入支持键盘滚动的轮播:

{{< doc-carousel label="发布亮点" >}}
{{< doc-card title="本地资源" >}}无需 CDN。{{< /doc-card >}}
{{< doc-card title="中英双语" >}}稳定的中英文路由。{{< /doc-card >}}
{{< /doc-carousel >}}

label 为辅助技术命名该区域。上一项/下一项按钮会本地化。信息不能只存在于屏幕外卡片中;禁用脚本后,轨道仍应可用。

param

输出页面参数;根据 Hugo 的 Page.Param 规则,在页面缺省时回退到站点配置:

OINK 版本 {{< param version >}}。

找不到参数会令构建失败。param 适合显示标量值,不应用于注入未经审查的 HTML。内部兼容短代码 _param 还会为旧内容执行带编号的占位符替换。

标签页

标签页用于组织 YAML/TOML/JSON 配置等同一信息的等价表示,不应隐藏连续步骤或互不相关的选择。

{{< tabpane text=true persist=lang >}}
{{< tab header="YAML" lang="yaml" >}} params: offlineSearch: true
{{< /tab >}} {{< tab header="TOML" lang="toml" >}} [params]
offlineSearch = true {{< /tab >}} {{< /tabpane >}}

选择状态保存在浏览器本地。persist 接受 headerlangdisabled。已弃用的 persistLang 不应出现在新内容中。

短代码细节

text=true 将内部内容渲染为正文而不是高亮代码;right=true 把标签对齐到末端;langEqualsHeader=true 根据标题推导语言标识。父级默认值可以由单个标签覆盖。

tabpane

父组件会校验布尔值和持久化参数、生成唯一 ID,并确保存在选中项。只有禁用的标题标签确实能提供有用分组信息时才使用它。

tab

tab 必须放在 tabpane 内部。它接受 headerselectedlanghighlighttextrightdisabled。只能选中一个标签。面向读者的标题需要翻译,语言标识则必须稳定。

卡片面板

旧版 cardpane/card 组合用于布局 Bootstrap 风格卡片。新的导航表面应优先使用 OINK 内容卡片,既有 Docsy 内容可以继续使用兼容组件。

card 短代码:文本内容

{{% cardpane %}}
{{% card header="说明" title="本地构建" footer="已验证" %}} Markdown
**正文**。 {{% /card %}} {{% /cardpane %}}

headertitlesubtitlefooter 接受渲染文本。并列卡片应保持简洁,不能用卡片取代标题结构。

card 短代码:程序代码

设置 code=true,并按需设置 lang/highlight

{{< cardpane >}} {{< card code=true header="Go" lang="go" >}}
fmt.Println("OINK") {{< /card >}} {{< /cardpane >}}

卡片组

cardpane 中相邻的卡片会形成响应式分组。应测试文字长度不一、移动端堆叠、代码溢出以及两种语言版本。

引入外部文件

readfile 短代码在构建期读取仓库文件,并将其渲染为 Markdown 或高亮代码。除非路径以 / 开头,否则路径相对于当前内容文件。

复用文档

{{% readfile "includes/installation.md" %}}

被引入的 Markdown 不是独立发布页面,因此不参加页面配对审计。如果共享正文面向读者,应有意识地创建并选择语言专属的 include 文件;Hugo 不会自动翻译 include。

安装

可复用片段应放在调用方附近的 includes/ 目录中。需要明确其所有权,并避免多层嵌套:读者和审阅者应能迅速找到源文件。

引入代码文件

{{< readfile file="includes/config.yaml" code="true" lang="yaml" >}}

code=true 会用 lang 高亮文件。绝不能引入机密、生成的凭据或不可信路径。

错误报告

找不到文件时构建会失败。draft=true 会把失败改为可见的草稿警告,只适合创作阶段,绝不能进入正式发布构建。

条件文本

conditional-text 根据 params.buildCondition 选择内容:

{{% conditional-text include-if="enterprise,preview" %}}
这段文字只出现在匹配的构建中。 {{% /conditional-text %}}

include-ifexclude-if 接受条件列表,同一条件不能同时出现在二者中。该功能适用于确实不同的发布变体,不应用来选择语言;多语言内容必须写入翻译后的页面文件。

6 - 分类法支持

使用标签、类别、标记等分类法组织内容。

OINK 在文档与博客分区中支持 Hugo 分类法。本页既展示默认布局,也可以用来测试生成链接的行为。

术语

使用分类法前,需要理解以下术语:

  • 分类法(Taxonomy):用于对内容进行分类的体系,例如标签、类别、项目、人物。

  • 术语(Term):分类法中的一个键。例如,在“项目”分类法中可以有“项目 A”和“项目 B”。

  • 值(Value):分配给某个术语的一项内容,例如属于特定项目的站点页面。

Hugo 文档提供了一个电影网站分类法示例

参数

项目配置文件中有多项参数可以控制分类法功能。Hugo 默认启用 tagscategories 分类法。要 禁用 分类法,请在项目配置中添加:

disableKinds = ["taxonomy"]
disableKinds: [taxonomy]
{
  "disableKinds": [ "taxonomy" ]
}

保持默认设置时,Hugo 会生成 tagscategories 的分类法页面。如果要使用其他分类法,需要在配置文件中定义。如果希望自定义分类法与默认的 tagscategories 并存,也必须把默认分类法一并写入配置。每种分类法都需要提供单数与复数标签。

下面的示例在默认 tagscategories 之外,又定义了 projects 分类法:

[taxonomies]
tag = "tags"
category = "categories"
project = "projects"
taxonomies:
  tag: tags
  category: categories
  project: projects
{
  "taxonomies": {
    "tag": "tags",
    "category": "categories",
    "project": "projects"
  }
}

项目配置中的以下参数可控制两类输出:文档和博客文章页显示的分类法术语,以及 OINK 右侧栏显示的“标签云”:

[params.taxonomy]
taxonomyCloud = ["projects", "tags"] # set taxonomyCloud = [] to hide taxonomy clouds
taxonomyCloudTitle = ["Our Projects", "Tag Cloud"] # if used, must have same length as taxonomyCloud
taxonomyPageHeader = ["tags", "categories"] # set taxonomyPageHeader = [] to hide taxonomies on the page headers
params:
  taxonomy:
    taxonomyCloud:
      - projects    # remove all entries
      - tags        # to hide taxonomy clouds
    taxonomyCloudTitle:   # if used, must have the same
      - Our Projects      # number of entries as taxonomyCloud
      - Tag Cloud
    taxonomyPageHeader:
      - tags        # remove all entries
      - categories  # to hide taxonomy clouds
{
  "params": {
    "taxonomy": {
      "taxonomyCloud": [
        "projects",
        "tags"
      ],
      "taxonomyCloudTitle": [
        "Our Projects",
        "Tag Cloud"
      ],
      "taxonomyPageHeader": [
        "tags",
        "categories"
      ]
    }
  }
}

以上设置只会在 OINK 右侧栏中显示 projectstags 的分类云(标题分别为“ Our Projects”和“Tag Cloud”),并在每个页面显示 tagscategories 分类法中已经分配的术语。

要禁用所有分类云,请设置 taxonomyCloud = [];如果不想显示已分配术语,请设置 taxonomyPageHeader = []

默认情况下,分类法的复数标签会用作分类云标题。可以通过 taxonomyCloudTitle 覆盖默认标题,但这样做时,必须为每个启用的分类云手工定义一个标题;taxonomyCloudtaxonomyCloudTitle 的长度必须相同。

如果没有设置 taxonomyCloudtaxonomyPageHeader,系统会为所有已定义分类法生成相应的分类云或已分配术语。

Partial

显示分类法时默认使用的 partial 经过专门设计,可以方便地在自定义布局中复用。

taxonomy_terms_article

taxonomy_terms_article partial 会显示一篇文章或页面(partial 参数 context,通常是当前页面或上下文 .)在指定分类法(partial 参数 taxo)中分配到的全部术语。

下面是在 layouts/docs/list.html 中为文档分区每个页面的 header 使用它的示例:

{{ $context := . }}
{{ range $taxo, $taxo_map := .Site.Taxonomies }}
  {{ partial "taxonomy_terms_article.html" (dict "context" $context "taxo" $taxo ) }}
{{ end }}

它会针对当前页面(或上下文)中的每个已定义分类法,输出一份包含全部已分配术语的列表:

<div class="taxonomy taxonomy-terms-article taxo-categories">
  <h5 class="taxonomy-title">Categories:</h5>
  <ul class="taxonomy-terms">
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/categories/taxonomies/"
        data-taxonomy-term="taxonomies"
        ><span class="taxonomy-label">Taxonomies</span></a
      >
    </li>
  </ul>
</div>
<div class="taxonomy taxonomy-terms-article taxo-tags">
  <h5 class="taxonomy-title">Tags:</h5>
  <ul class="taxonomy-terms">
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/tags/tagging/"
        data-taxonomy-term="tagging"
        ><span class="taxonomy-label">Tagging</span></a
      >
    </li>
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/tags/structuring-content/"
        data-taxonomy-term="structuring-content"
        ><span class="taxonomy-label">Structuring Content</span></a
      >
    </li>
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/tags/labelling/"
        data-taxonomy-term="labelling"
        ><span class="taxonomy-label">Labelling</span></a
      >
    </li>
  </ul>
</div>

taxonomy_terms_article_wrapper

taxonomy_terms_article_wrappertaxonomy_terms_article 的包装 partial,只有一个 context 参数(通常是当前页面或上下文 .)。它会检查项目 hugo.tomlhugo.yamlhugo.json 中的分类法参数,遍历 taxonomyPageHeader 中列出的全部分类法;如果没有设置 taxonomyPageHeader,则遍历页面定义的全部分类法。

taxonomy_terms_cloud

taxonomy_terms_cloud partial 会显示站点(partial 参数 context,通常是当前页面或上下文 .)在指定分类法(partial 参数 taxo)中使用的全部术语,并使用 title 参数作为标题。

下面是在 taxonomy_terms_clouds partial 中显示所有已定义分类法及其术语的示例:

{{ $context := . }}
{{ range $taxo, $taxo_map := .Site.Taxonomies }}
  {{ partial "taxonomy_terms_cloud.html" (dict "context" $context "taxo" $taxo "title" ( humanize $taxo ) ) }}
{{ end }}

对于 categories 分类法,它会生成以下 HTML 标记:

<div class="taxonomy taxonomy-terms-cloud taxo-categories">
  <h5 class="taxonomy-title">Cloud of Categories</h5>
  <ul class="taxonomy-terms">
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/categories/category-1/"
        data-taxonomy-term="category-1"
        ><span class="taxonomy-label">category 1</span
        ><span class="taxonomy-count">3</span></a
      >
    </li>
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/categories/category-2/"
        data-taxonomy-term="category-2"
        ><span class="taxonomy-label">category 2</span
        ><span class="taxonomy-count">1</span></a
      >
    </li>
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/categories/category-3/"
        data-taxonomy-term="category-3"
        ><span class="taxonomy-label">category 3</span
        ><span class="taxonomy-count">2</span></a
      >
    </li>
    <li>
      <a
        class="taxonomy-term"
        href="//localhost:1313/categories/category-4/"
        data-taxonomy-term="category-4"
        ><span class="taxonomy-label">category 4</span
        ><span class="taxonomy-count">6</span></a
      >
    </li>
  </ul>
</div>

taxonomy_terms_clouds

taxonomy_terms_cloudstaxonomy_terms_cloud 的包装 partial,只有一个 context 参数(通常是当前页面或上下文 .)。它会检查项目配置中的分类法参数,遍历 taxonomyCloud 列出的全部分类法;如果没有设置 taxonomyCloud,则遍历页面定义的全部分类法。

分类法的多语言支持

对于多语言站点,分类法术语只会在各自语言站点内计数和链接。分类法配置参数也可以按语言分别调整。

7 - 分析、用户反馈与 SEO

配置可选分析和反馈,同时提供有用的 SEO 元数据。

OINK 默认不会连接分析、表单、评论或广告服务。这些集成属于站点决策:必须显式启用、记录数据边界,并根据用户与站点所在司法辖区提供必要的同意机制或政策说明。

添加分析

Hugo 为分析服务提供嵌入模板。站点配置 Google Analytics 后,页面浏览量与自定义事件等浏览器使用信息会发送给 Google。这与完全网络隔离的运行环境不兼容,也可能不符合严格的同源内容安全策略(CSP)。

配置

取得站点的 Google Analytics measurement ID,然后使用 Hugo 当前的服务配置:

services:
  googleAnalytics:
    id: G-YOUR-ID

不要同时设置已经弃用的顶层 googleAnalytics 键。通常只有 Hugo production 环境才会输出分析代码。发布前,请构建生产预览,并检查 HTML 与浏览器网络日志。

禁用分析后,OINK 不会发起 Google Analytics 请求。应彻底删除相关配置,而不是填写虚假 ID。

用户反馈

OINK 可以在文档页底部显示“本页是否有帮助?”小组件。它提供 两个操作,随后显示配置好的响应;响应通常包含创建文档 issue 的链接。

页面询问内容是否有帮助,并提供“是”和“否”两个按钮。
图 1:页面反馈组件

即使不启用分析,响应仍然可以发挥作用:它可以把读者引导到 issue 模板、讨论区、电子邮箱或站点自有的其他反馈渠道。只有站点配置了适当目标后,才会发生数据收集和事件上报。

反馈数据有什么用?

应结合上下文理解反馈,不能把单一分数当作结论。访问量高且反复收到负面反馈的页面是值得优先复查的候选;高评分页面则可能揭示值得在其他页面验证的模式。

应尽可能采用聚焦的编辑变更。例如,只更新一篇过时教程,或者把一小组页面的代码示例提前,然后在合适的时间范围内比较反馈。同时记录发布事件、流量变化、支持事件和其他可能解释变化的因素。

反馈只能提供方向性证据,不能取代用户研究、无障碍评审、支持数据或技术验证。

配置

OINK 默认关闭该小组件。请设置全局默认值,并配置本地化响应。英文配置如下:

params:
  ui:
    feedback:
      enable: false
languages:
  en:
    params:
      ui:
        feedback:
          yes: >-
            Glad to hear it! Please <a
            href="https://github.com/OWNER/REPOSITORY/issues/new">tell us how we
            can improve</a>.
          no: >-
            Sorry to hear that. Please <a
            href="https://github.com/OWNER/REPOSITORY/issues/new">tell us how we
            can improve</a>.

简体中文字符串放在 languages.zh.params 下:

languages:
  zh:
    params:
      ui:
        feedback:
          yes: >-
            很高兴本页对你有帮助!欢迎<a
            href="https://github.com/OWNER/REPOSITORY/issues/new">告诉我们如何继续改进</a>。
          no: >-
            很抱歉本页没有解决问题。请<a
            href="https://github.com/OWNER/REPOSITORY/issues/new">告诉我们缺少什么</a>。

可见响应 HTML 属于可信站点配置。内容应保持精简,链接需要经过评审,并且不能插入不可信值。

配置 Google Analytics 后,小组件可以发送自定义 page_helpful 事件。正面操作使用 params.ui.feedback.max_value(默认为 100),负面操作使用 0。

访问反馈数据

使用 Google Analytics 时,可以在服务商的事件报告中查看 page_helpful,并按需创建页面级报告。没有事件并不一定表示没有用户反馈;也可能是分析被阻止或禁用、用户没有同意,或者所选时间范围不正确。

不要仅仅为了显示小组件就启用分析。站点可以保留响应和链接体验,同时关闭事件收集。

在单个页面覆盖反馈设置

在页面 Front Matter 中设置 feedback。页面设置可从任一方向覆盖全局默认值:

---
title: 反馈示例
feedback: true
---

全局默认开启时,可用 feedback: false 隐藏单个页面的小组件。为保持兼容,未设置 feedback 时,hide_feedback: true 仍会隐藏小组件。

设置所有页面的默认值

设置以下站点参数。OINK 默认值为 false;只有大多数文档页都应显示小组件时,才将其设为 true

params:
  ui:
    feedback:
      enable: false

使用 Fabform 添加联系表单

Fabform 和类似托管表单端点都是可选在线服务。创建账户并评审其数据处理方式后,站点可以把表单提交到分配的端点:

<form action="https://fabform.io/f/{form-id}" method="post">
  <label for="email">电子邮箱</label>
  <input id="email" name="email" type="email" autocomplete="email" />
  <button type="submit">提交</button>
</form>

请替换 {form-id}、翻译可见标签、加入隐私说明,并提供错误与成功状态。该表单无法离线使用。如果站点必须让提交内容留在自身边界内,应优先使用本地或第一方端点。

搜索引擎优化元数据

OINK 会按以下优先级为每个页面选择 HTML meta description:

  1. 页面 front matter 中的 description
  2. 对于非索引页,使用 Hugo 计算出的页面摘要;
  3. params 中的站点描述。

请为每种语言编写精炼且针对当前页面的描述。不要把英文描述复制到中文页面。搜索元数据无法弥补内容单薄、重复或不准确的问题。

主题还会根据 Hugo 页面译文输出 canonical 与备用语言链接。请使用正确的生产 baseURL、稳定的译文路由和显式译文标题 ID。只有主题尚未提供某类 meta 标签时,才应通过站点的 layouts/_partials/hooks/head-end.html 覆盖添加。

底层服务与内容概念请参阅 Hugo 的 Google Analytics 配置页面摘要和 Google 的 SEO 入门指南

8 - 搜索

配置本地多语言搜索,或显式启用在线服务商。

OINK 默认并推荐使用本地搜索。Hugo 会为每种语言生成独立索引;主题从同源资源提供 Lunr 及其 CJK 回退。站点无需公共爬虫、外部账户、CDN 或网络连接,即可完成构建和搜索。

Google Custom Search 与 Algolia DocSearch 仍作为兼容的在线集成保留。它们默认关闭;只有站点明确接受相应的外部请求、索引方式、可用性与隐私边界时,才应启用。

同一时间只能启用一种搜索实现。

使用 Lunr 的本地搜索

hugo.yaml 中启用本地搜索:

params:
  offlineSearch: true

不要同时配置 gcs_engine_idparams.search.algolia。生产构建完成后,输出中会为每种语言生成一个索引,例如:

offline-search-index.en.json
offline-search-index.zh.json

浏览器加载当前语言的索引,并在不离开页面的情况下显示结果。中文内容使用 OINK 的 CJK 回退,不依赖以空格分词。

测试前构建索引

启动预览前先执行常规构建:

hugo --gc
hugo server --disableFastRender

如果索引变化时 server 已经在运行,请将其重启。对于子路径部署,请确认浏览器从配置的 baseURL 下请求索引,而不是从域名根目录请求。

配置结果摘要与数量限制

设置摘要长度和最大结果数:

params:
  offlineSearch: true
  offlineSearchSummaryLength: 120
  offlineSearchMaxResults: 12

所选限制应确保搜索对话框在移动设备上保持流畅。摘要用于帮助发现内容,不能替代认真编写的页面描述。

排除页面

在页面 front matter 中设置 exclude_search: true

---
title: Internal index
exclude_search: true
---

该设置适用于工具页、重复页、生成页或测试页。不要仅仅因为当前译文不完整就排除页面;应修复译文。

设置结果面板样式

结果面板会随内容扩展。站点可以在 assets/scss/_styles_project.scss 中限制宽度:

.td-offline-search-results {
  max-width: 46rem;
}

覆盖搜索样式时,必须保留键盘焦点、可见选中状态、移动端宽度和深色模式对比度。

搜索入口

OINK 会在品牌外壳中提供搜索入口,也可以在侧栏显示输入框。如果要隐藏侧栏输入框,同时保留主搜索入口,请配置:

params:
  ui:
    sidebar_search_disable: true

外壳的打开与关闭控件会向辅助技术暴露对话框关系和状态。自定义实现必须保留这些语义。

搜索始终停留在当前语言。请验证:

  • 每种已发布语言都有自己的索引;
  • 译文标题、描述和正文出现在对应索引中;
  • 结果 URL 包含正确的语言前缀;
  • 英文结果不会通过内容回退取代中文结果;
  • 结果页上的语言选择器能前往对应译文,或按文档规则回退到语言首页。

中文搜索出现故障时,应先检查生成的中文 JSON,再考虑修改分词。索引缺失或只包含英文,通常属于内容或构建配置问题。

Google Custom Search Engine(GCSE)通过 Google 索引搜索公开站点。它需要已经部署且允许爬取的生产站点,并会把查询发送给第三方服务。

Google Programmable Search 中创建搜索引擎后,添加搜索结果页:

---
title: 搜索结果
layout: search
---

随后配置搜索引擎 ID:

params:
  gcs_engine_id: YOUR_ENGINE_ID
  offlineSearch: false

为每种支持语言创建译文结果页;必要时使用适合该语言的搜索引擎配置。删除 gcs_engine_id 即可禁用 GCSE。

消费站点应在隐私政策中说明外部请求和隐私影响。GCSE 无法在网络隔离部署中使用。

Algolia DocSearch(可选)

Algolia DocSearch 为符合条件的公开文档站点提供托管爬虫和交互式结果面板。取得项目的 application ID、搜索 API key 和索引名称后,配置:

params:
  offlineSearch: false
  search:
    algolia:
      appId: YOUR_APP_ID
      apiKey: YOUR_SEARCH_API_KEY
      indexName: YOUR_INDEX_NAME

只能使用公开的只读搜索 key,绝不能使用管理 key。爬虫规则、语言 facet、索引更新与外部服务声明应与站点配置一同维护。该集成有意与本地优先默认值分离。

可以覆盖主题 partial layouts/_partials/algolia/head.htmllayouts/_partials/algolia/scripts.html,实现站点专属集成。空的覆盖文件会禁用对应主题 partial。

如果现有选项都不合适,站点可以替换搜索输入、结果行为与样式。应尽量复用外壳的对话框与无障碍合同。除非自定义代码与服务商无关,并且能被多个产品复用,否则应保留在站点层。

自定义在线服务商必须显式启用,并说明网络、隐私、索引、故障与离线行为。自定义本地服务商必须从站点或主题发布全部运行时资源,并遵守语言和 baseURL 边界。

9 - 添加内容

在 OINK 中组织和编写中英双语文档与博客内容。

OINK 沿用 Hugo 的内容模型:Markdown 承载信息,Front Matter 保存页面元数据,布局则把二者渲染成静态站点。本指南说明随项目提供的中英双语样例站采用的内容约定。

内容根目录

站点内容位于 content/ 目录下。多语言站点既可以分别使用 content/en/content/zh/ 等内容根目录,也可以在同一棵挂载目录中使用语言后缀。本仓库采用后一种形式:

content/docs/content/
├── adding-content.md
└── adding-content.zh.md

英文文件是源页面,.zh.md 文件是对应的简体中文译文。Hugo 解析语言后缀后,二者具有相同的逻辑路径。

生成文件以及必须逐字节复制的文件不应放入内容树,而应放入 static/;详见添加静态内容

内容分区与模板

内容根目录下的每个一级目录都是 Hugo 分区。OINK 提供以下布局:

  • docs:带分区树、目录、面包屑、上一篇/下一篇导航和仓库链接的文档页;
  • blog:带日期、分类元数据、Feed 和时间倒序列表的文章页;
  • community:展示项目与贡献者链接的社区页;
  • 默认页面:不显示文档侧边栏的落地页。

Hugo 根据内容所属分区选择布局,因此 content/docs/ 下的页面会使用 docs 布局。只有确实要复用其他分区布局时,才在 Front Matter 中设置 type

自定义分区

在内容根目录下新建目录;默认布局无法满足需求时,再为页面指定类型:

---
title: 架构决策
description: 项目已经采纳的设计决策。
type: docs
weight: 30
---

如果某项行为适用于整个分区,应在 _index.mdcascade 中设置共享值,避免每页重复。只有现有 OINK 布局与 Partial 均不适用时,才在项目的 layouts/ 下新增布局。

以文档为根的站点

EXPERIMENTAL

以文档为主的站点可以把 docs 分区发布到 URL 根路径,同时仍将源码保存在 content/.../docs/ 下:

permalinks:
  page:
    docs: /:sections[1:]/:slug/
  section:
    docs: /:sections[1:]

此时,文档分区落地页会成为站点首页。请为每种语言的物理站点根索引添加以下 Front Matter,使其仍可作为链接使用,同时不会争抢相同的输出路径:

build: { render: link }

检查路径冲突

文档会与博客、社区及其他分区共享 URL 根路径。构建时启用 --printPathWarnings,并在发布前解决所有重复目标:

hugo --printPathWarnings

旧版纯文档配置

旧版 Docsy 示例曾通过 Front Matter 的 cascade 强制设置页面类型。迁移到基于永久链接的文档根配置时,应删除这项变通设置,否则首页与分区布局可能出现不一致的解析结果。

页面 Front Matter

Front Matter 是用 YAML、TOML 或 JSON 编写的页面元数据。OINK 样例站使用 YAML:

---
title: Local-first architecture
linkTitle: Local-first
description: How OINK removes browser and build-time CDN dependencies.
weight: 20
date: 2026-08-08
tags: [architecture, offline]
---

title 是实际所需的最小字段。对于持续维护的文档,还应提供简洁的 description 供搜索和页面元数据使用;顺序有意义时应设置 weight。只有导航标签需要更短文本时才使用 linkTitle

译文应翻译面向读者的元数据,同时保留结构性取值:

---
title: 本地优先架构
linkTitle: 本地优先
description: OINK 如何消除浏览器端与构建期的 CDN 依赖。
weight: 20
date: 2026-08-08
tags: [架构, 离线]
---

不要翻译字段名、短代码名称、配置项、文件路径或稳定标识符。

文档页与博客页会在站点页脚上方显示紧凑的元数据区域。最后修改日期取自 Hugo 的 .Lastmod 值;以下两个可选 Front Matter 字段用于补充来源说明:

lastmod: 2026-08-09
upstream_attribution: https://upstream.example/docs/page/
downstream_modified: true

upstream_attribution 链接到上游原文及其署名信息;downstream_modified: true 表示下游项目修改过本页。某项说明不适用时,请省略对应字段。

页面正文

除非布局确实要求 HTML,否则页面应使用 Markdown。Hugo 通过 Goldmark 渲染 Markdown,并支持属性、脚注、表格、任务列表、渲染钩子和围栏代码块。

Markdown

即使脱离渲染后的站点,源码也应保持可读:

  • 使用 ATX 标题(## 标题);
  • 列表、块和围栏代码前后保留空行;
  • 代码语言已知时必须标注;
  • 使用能说明去向的链接文本和图片替代文本;
  • 普通正文按便于审阅的宽度换行,但不要重排代码或 URL。

OINK 为块引用告警以及 Mermaid、数学公式、化学公式、Markmap 和 PlantUML 代码块提供渲染钩子。详见图表与公式

标记、短代码与内容功能

普通正文优先使用标准 Markdown。需要标签页、卡片、终端录像、API 查看器或安全图表等有实际行为的组件时,再使用短代码。短代码属于内容契约的一部分:应在两种语言中核对其参数,不要把渲染后的 HTML 复制到译文。

告警

OINK 支持 GitHub 风格的块引用告警,也支持可选的 Obsidian 风格标题:

> [!TIP]
>
> 每次发布前都要运行翻译审计。

> [!WARNING] 必须使用稳定锚点
>
> 译文标题必须保留英文页面渲染后的 ID。

语义类型包括 NOTETIPIMPORTANTWARNINGCAUTION,以及与 Bootstrap 兼容的类型和 NB。告警应节制使用:关键信息在屏幕阅读器和打印版中也必须成立。外观设置参见告警

稳定公开路由使用根路径相对链接,相邻页面或页面包资源使用普通相对链接。Hugo 的 refrelref 短代码可以校验内容引用,并处理语言和永久链接规则:

[配置]({{< ref "/docs/oink/configuration" >}})

编写双语页面时:

  • 链接到逻辑页面,不要直接链接 .zh.md 文件名;
  • 片段 ID 应保持语言中立;
  • 验证两种语言能否解析到相同片段;
  • 目标必须相对于当前主机时使用 relref

调整路由或标题后,应运行站内链接检查。

内容风格

任务型文档应使用直接、明确的语言:先介绍概念,再给出配置;明确说明默认值;区分本地构建验证、部署与正式发布。中文版遵循 oink.pgsty.com/TRANSLATION.md 中的术语与排版规则。

页面包

独立页面只有一个 Markdown 文件;叶子页面包则由 index.md 和页面资源组成:

content/docs/tutorial/
├── index.md
├── index.zh.md
├── architecture.svg
└── example.yaml

两种语言的页面可以共用同一图片和下载文件。在单主机多语言站点中,Hugo 通常会在语言版本之间共享页面资源,因此不要复制完全相同的二进制资源。只有图片包含需要翻译的文字时才制作本地化版本,并为资源添加清晰的语言后缀。

包含子页面的分区使用分支页面包(_index.md),带资源的末端页面使用叶子页面包(index.md)。

添加文档与博客文章

每个持续维护的英文页面都应在同一目录下配有中文页面:

guide.md
guide.zh.md

页面包则将 index.mdindex.zh.md 配对。除非语言差异确有必要,否则二者的路由元数据、日期、权重、别名和资源声明应保持一致。

组织文档

目录应反映读者看到的信息架构,而不是实现代码的包结构。每个文档子分区都需要 _index.md_index.zh.md。子页面会按 weight 排列在侧边栏中,权重相同时再使用配置的后备顺序。

层级应尽量浅。页面面向独立任务或受众时才拆分,不要仅仅因为文件较长而拆分。详见组织内容

文档分区落地页

文档分区的 _index.md 默认会渲染子页面摘要。使用:

simple_list: true

可以改为紧凑列表;使用:

no_list: true

可以关闭自动列表。每种语言都应提供本地化标题和描述,并保持结构选项一致。

组织博客文章

博客文章既可以直接放在 blog/ 下,也可以按年份或分类建目录。OINK 使用日期目录,并为每篇文章配对:

blog/2026/
├── oink-release.md
└── oink-release.zh.md

文章通常包含:

---
title: OINK 1.0
description: A local-first Docsy distribution.
date: 2026-08-08
author: OINK maintainers
tags: [release]
---

不同语言版本的发布日期与作者身份应保持一致。标题、描述、分类标签、图注和正文需要翻译;提交 ID、发布标签、命令和 URL 不应翻译。

使用一级落地页

默认布局适用于首页、产品概览和其他不需要文档侧边栏的入口页。

自定义样例站页面

随项目提供的首页是 content/_index.md,其中文译文是 content/_index.zh.md。它与 OINK 其余页面使用同一套本地资源和主题流水线。品牌调整应修改站点内容与项目资源,不要为了品牌外观去编辑已经纳管的运行时文件。

构建自己的落地页

使用标准 Markdown 和blocks/* 短代码组合落地页。关键信息必须保留为文本,行动链接应说明实际去向,并在两种语言中分别测试移动端和桌面端布局。

添加社区页面

创建 community/_index.mdcommunity/_index.zh.md。社区布局会读取 params.links.userparams.links.developer

params:
  links:
    user:
      - name: 用户论坛
        url: https://community.example.org/
        icon: fa-solid fa-comments
        desc: 提问并分享解决方案
    developer:
      - name: GitHub
        url: https://github.com/pgsty/oink
        icon: fa-brands fa-github
        desc: 源码、议题与拉取请求

条目可以设置 rel;对于外部 HTTP 链接,OINK 也会按需补充 noopener。贡献指南不在约定的文档路径时,请在社区页 Front Matter 中设置 params.contributingUrl

添加静态内容

static/ 下的文件不会经过 Markdown 渲染或指纹处理,而是原样复制到发布根目录:

static/reference/api/index.html

会发布为 /reference/api/index.html。该目录适合外部生成的参考站点、验证文件以及要求稳定文件名的下载内容。需要缩放、指纹或页面包相对寻址的资源,应优先使用页面资源或 Hugo Pipes。

OINK 的浏览器运行时有意从主题或站点自身提供。新增依赖库时,必须本地纳管并锁定版本,在 theme/VENDOR.json 中登记,而且不得引入隐式 CDN 后备地址。

RSS Feed

Hugo 会为首页和列表分区生成 Feed。只有站点确实没有 Feed 消费者时才全局关闭:

disableKinds: [RSS]

分区声明自定义输出格式时,应显式保留 RSS:

outputs:
  section: [HTML, RSS, print]

检查每种语言生成的 Feed URL,并核对标题、摘要、日期、规范 URL 与 hreflang 关系。

站点地图

Hugo 默认生成 sitemap.xml。站点级设置如下:

sitemap:
  changefreq: monthly
  filename: sitemap.xml
  priority: 0.5

页面可以覆盖这些值:

---
title: 发布说明
sitemap:
  priority: 0.8
---

应把 changefreqpriority 视为提示而非承诺。部署前应排除草稿、私有内容和非规范副本,并检查每种发布语言生成的站点地图。

10 - 图表与公式

在页面中添加本地图表、思维导图与科学公式。

OINK 支持 KaTeX、Mermaid、Markmap、PlantUML 和 Diagrams.net。KaTeX、Mermaid 与 Markmap 使用构建期能力或主题随附的同源资源。PlantUML 和 Diagrams.net 编辑器需要显式配置服务端点;主题不会静默使用公共服务。

使用 KaTeX 支持 LaTeX

KaTeX 可以在 Web 上渲染 TeX 数学公式。Hugo 内置的 KaTeX 支持可以在构建期间渲染公式,因此读者不需要连接远程数学服务。

行内公式

行内公式使用 Goldmark 中配置的 passthrough 分隔符。条件允许时,应把公式前后的空格与标点留在公式之外。

独立显示公式

使用 math 代码块独立显示公式:

```math
E = mc^2
```
E=mc2E = mc^2

启用 KaTeX 支持

mathchem 代码块会自动使用主题渲染钩子。对于行内公式和使用分隔符的公式,请启用 Goldmark 的 passthrough 扩展,并设置适合站点的分隔符。随仓库提供的 oink.pgsty.com 配置展示了方括号、双美元符号和圆括号分隔符。

启用 passthrough 扩展

相关 YAML 结构如下:

markup:
  goldmark:
    extensions:
      passthrough:
        enable: true
        delimiters:
          block: []
          inline: []

请根据 Hugo 文档填写分隔符数组。所选分隔符不能与站点正文或代码冲突,并且必须在所有构建环境中保持一致。

添加 passthrough 渲染钩子

对于使用分隔符的数学公式,请在站点中创建 layouts/_markup/render-passthrough.html

{{ partial "scripts/math.html" . }}

也可以把钩子放在对应布局目录下,将其限制到某种内容类型或某个分区。限制作用域可以避免把无关内容当作数学 passthrough 处理。

化学方程式与物理单位

Hugo 内置 KaTeX 支持 mhchem 扩展。化学方程式可以使用 chem 代码块;同一扩展也支持物理单位。方程式与单位语法请参阅 mhchem 手册

使用 Mermaid 绘图

Mermaid 可以在浏览器中把文本定义转换为图表。使用 mermaid 代码块:

```mermaid
flowchart LR
  源码 --> Hugo --> 静态文件
```
flowchart LR
  源码 --> Hugo --> 静态文件

主题会检测代码块、发布固定版本的本地 Mermaid 运行时,并且在该页只加载一次。不使用 Mermaid 的页面不会加载运行时。

站点级 Mermaid 设置位于 params.mermaid

params:
  mermaid:
    theme: neutral
    flowchart:
      diagramPadding: 6

每幅图也可以通过 Mermaid 支持的 front matter 覆盖设置。图表源码应保持可读,并同时测试深浅色模式。对于图表无法渲染时仍必须传达的信息,请提供相邻正文。

使用 PlantUML 绘制 UML 图

PlantUML 支持时序图、用例图、类图、状态图和其他面向 UML 的图表。plantuml 代码块包含图表源码:

```plantuml
actor Reader
participant Browser
participant "PlantUML endpoint" as Server
Reader -> Browser: Open page
Browser -> Server: Request encoded diagram
Server --> Browser: SVG
```

PlantUML 需要渲染端点。只有在配置了获准使用的本地或显式远程服务后才应启用:

params:
  plantuml:
    enable: true
    theme: default
    svg_image_url: https://plantuml.internal.example/plantuml/svg/
    svg: false

浏览器会把编码后的图表源码发送给端点。请评审其保密性、可用性、CSP 与离线影响。网络隔离站点应使用内部端点或提交预渲染图片,默认配置不能指向公共演示服务器。

使用 Markmap 支持思维导图

Markmap 可以把 Markdown 大纲转换为交互式思维导图:

```markmap
# 本地优先
## 构建
- Hugo Extended
## 浏览器
- 本地脚本
- 本地字体
```
# 本地优先
## 构建
- Hugo Extended
## 浏览器
- 本地脚本
- 本地字体

需要时可以全局启用:

params:
  markmap:
    enable: true

运行时采用固定版本并从本地提供。底层大纲本身也应有用,同时不要依赖只能通过指针完成的交互。

使用 Diagrams.net 绘图

Diagrams.netdraw.io)可以导出包含可编辑图表副本的 SVG 与 PNG。显式配置编辑器端点后,OINK 可以检测这些图片并显示 编辑 操作。

params:
  drawio:
    enable: true
    drawio_server: https://drawio.internal.example/

导出时请启用 Include a copy of my diagram。页面可以离线显示导出图片,但打开编辑器需要连接配置的服务。编辑器保存时会把更新后的文件下载到浏览器,不会直接写入文档仓库。

公共 Diagrams.net 端点属于在线集成。如果编辑过程必须留在组织内部,请部署获准使用的自托管编辑器,并让 drawio_server 指向它。

资源与创作检查清单

  • 当可评审 diff 很重要时,优先使用文本图表。
  • 为关键信息提供替代文字或相邻正文。
  • 测试深浅色、移动端、打印和减少动态效果模式。
  • theme/VENDOR.json 中固定本地运行时,并且只在使用时加载。
  • 绝不能把机密写入会发送给服务端点的图表源码。
  • 无法接受在线渲染器时,使用预渲染输出。
  • 在子路径 baseURL 下验证所有资源与端点 URL。

11 - 外观与风格

定制 OINK 的本地优先视觉系统、主题、字体、代码样式与布局。

OINK 在 Bootstrap 与 Docsy 基础上提供完整的视觉系统,并将字体、图标、样式和浏览器端代码全部本地化。使用方无需重建 Node 依赖树,就能通过设计变量和项目样式完成定制。

项目样式

Hugo Extended 通过 Hugo Pipes 编译主题 SCSS。项目覆盖项会进入同一个资源包,因此生产构建可以对一份同源样式表完成压缩、指纹和完整性校验。

项目样式文件

在站点的 assets/scss/ 目录中覆盖以下文件:

文件 用途
_variables_project.scss 在 Bootstrap 与 OINK 默认值之前设置变量
_variables_project_after_bs.scss 设置依赖 Bootstrap 定义的变量或映射
_styles_project.scss 在主题组件样式之后加载项目选择器

先从最小覆盖项开始:

// assets/scss/_variables_project.scss
$primary: #315f8f;
$secondary: #b4762e;
// assets/scss/_styles_project.scss
.td-content {
  --td-content-max-width: 78ch;
}

普通品牌定制不要直接修改纳管的 Bootstrap、Font Awesome 或本地字体文件。主题更新会覆盖这些改动,也会模糊依赖边界。

高级样式定制

OINK 的 SCSS 导入顺序如下:

  1. Bootstrap 函数;
  2. 项目变量;
  3. OINK 默认值与 Bootstrap;
  4. Bootstrap 之后的项目变量;
  5. OINK 组件与本地品牌层;
  6. 项目样式。

稳定的设计决策应通过变量或 CSS 自定义属性表达。没有合适设计变量时才覆盖选择器,而且作用域应尽量缩小到具体组件。许多颜色会随主题变化,因此必须检查浅色与深色输出。

⚠️ 重置内部样式

OINK 的内部 Partial 并不是公开 Sass API。单独导入或屏蔽内部文件会让站点耦合到仓库布局和导入顺序。产品确实需要完全不同的页面框架时,应覆盖 Hugo 布局或有意识地维护主题分支,而不是重置整份样式表。

额外样式

隔离的第三方 CSS 可以通过钩子发布为本地资源:

{{ $extra := resources.Get "css/extra.css" | minify | fingerprint }}
<link rel="stylesheet" href="{{ $extra.RelPermalink }}"
  integrity="{{ $extra.Data.Integrity }}" crossorigin="anonymous">

将模板放在 layouts/partials/hooks/head-end.html。如果规则属于站点设计系统,应优先写入项目 SCSS 文件。绝不能把远程样式表当作隐式后备资源。

颜色与颜色主题

主题各处都可以使用 Bootstrap 语义颜色与 OINK 品牌设计变量。语义名称比具体色值更能说明用途。

站点颜色

在编译前设置 Bootstrap 变量:

$primary: #315f8f;
$secondary: #b4762e;
$success: #2c7a4b;
$warning: #9a6700;
$danger: #b42318;

OINK 的标准品牌层还公开 --td-brand-elev--td-brand-silk--td-brand-copper--td-brand-header-bg--td-brand-mark-gradient 等 CSS 属性。应同时在 :root[data-bs-theme='dark'] 中成对覆盖:

:root {
  --td-brand-copper: #a66722;
}

[data-bs-theme='dark'] {
  --td-brand-copper: #e0a35c;
}

浅色/深色主题与模式支持

颜色 主题 是组件采用的配色方案,颜色 模式 则是整个站点当前处于浅色还是深色状态。OINK 使用 Bootstrap 的 data-bs-theme="light|dark" 属性,并把读者明确选择的模式保存在浏览器本地存储中。没有明确选择时,站点跟随 prefers-color-scheme

每个自定义组件都必须为两种模式定义可读状态,包括悬停、焦点、禁用、选中和代码颜色。不能只用颜色传递含义。

浅色/深色模式

样例站默认启用颜色模式支持并显示选择器:

params:
  ui:
    showLightDarkModeMenu: true

选择器会在页面进入正常交互前更新文档,以减少错误主题闪烁。OINK 的脚本从本地加载,不会联系外部服务。

为站点选择主题或颜色模式

多数站点应使用默认自动行为。只有完整视觉系统已经在某种模式下通过测试,而且读者确实不需要另一种模式时,才应强制指定。截图不足以完成验证:还要检查真实正文、表格、告警、表单、图表、代码和焦点指示器。

禁用深色模式

如需禁用深色模式并隐藏菜单:

params:
  ui:
    showLightDarkModeMenu: false

实验值 enable-only (experimental) 会启用主题感知样式,但不显示选择器。该配置面仍可能变化,只能作为过渡选项使用。

选择具有良好对比度的颜色

所有组件状态都应满足 WCAG 对比度要求,并以浏览器实际计算后的颜色为准,包括叠加在图片上的半透明图层。作为工作基线,普通文字的对比度至少为 4.5:1,大号文字至少为 3:1;焦点和非文本界面指示器同样需要足够对比度。自动化工具可以发现常见问题,但仍需进行键盘和人工视觉审查。

字体

OINK 不会拉取 Google Fonts。主题使用的 Open Sans、Chakra Petch、IBM Plex Mono 与 Font Awesome 字体文件均保存在本地。由于历史原因,旧版 Sass 变量 $td-enable-google-fonts 实际控制的是随主题提供的 Open Sans 字体。

_variables_project.scss 中设置字体:

$td-enable-google-fonts: true;
$font-family-sans-serif: 'Noto Sans SC', 'Open Sans', system-ui, sans-serif;
$font-family-monospace: 'IBM Plex Mono', ui-monospace, monospace;

新增字体时,应制作所需子集并自行托管,包含必要字形,设置 font-display: swap,在 theme/VENDOR.json 中记录许可证,并测试 CJK 后备字体。页面渲染不能依赖字体 CDN。

CSS 工具类

在允许原始 HTML 的 Markdown 和布局中可以使用 Bootstrap 工具类。内容优先使用语义化 Markdown 与 OINK 短代码;工具类只适合在不同断点下仍易于理解的小范围表现调整。项目级模式应写入 _styles_project.scss

代码块

OINK 默认支持 Hugo Chroma,并提供本地纳管的 Prism 兼容选项。一个站点应统一选择一种高亮器;同时启用会产生重复标记或样式。

使用 Chroma 进行代码高亮

Chroma 在 Hugo 构建期间运行,不需要浏览器端高亮器。代码块应指定语言:

```go
fmt.Println("hello")
```

Chroma 基础样式配置

在 Hugo 中配置标记渲染:

markup:
  highlight:
    guessSyntax: false
    noClasses: false
    lineNos: false

OINK 使用基于 class 的输出,以便浅色和深色模式采用不同样式。重新生成配色时,应将 CSS 保存在本地,并结合品牌背景完成审查。

浅色/深色代码样式及其他配置

主题在 theme/assets/scss/td/chroma/ 中提供两套 Chroma 配色,并按模式应用。项目覆盖项应在相应主题属性下定位 .chroma,不要硬编码全局背景。

选择控制台代码块内容

终端记录使用 console。OINK 会调整提示符和输出的选中行为,使读者复制命令时不会带上装饰性提示符。命令与输出应各占一行,而且不能只靠颜色区分。

未指定语言的代码块

没有标签的围栏会渲染为纯代码。只有确实不存在相应语法时才这样做;命令会话应标为 consolebash,不要让 Chroma 猜测。

复制到剪贴板

除非 params.disable_click2copy_chroma 为 true,否则 Chroma 会显示复制按钮。已部署站点中的剪贴板访问需要安全上下文。该控件必须支持键盘操作,而且不应复制行号或提示符。

使用 Prism 进行代码高亮

设置:

params:
  prism_syntax_highlighting: true

即可使用 OINK 本地提供的 prism.jsprism.css。这是面向既有站点的兼容选项;若要尽量减少浏览器负担,优先使用 Chroma。

没有语言的代码块

Prism 同样会把没有标签的代码块当作纯文本。应补充正确的语言 class,而不是启用启发式检测。

扩展 Prism 语言或插件

构建并纳管准确的 Prism 资源包,通过受控主题变更替换本地文件,记录版本与许可证,并添加覆盖该语言或插件的 Fixture。运行时不得从 CDN 拉取 Prism 组件。

OINK 导航栏包含项目标识、主菜单、按需显示的版本与语言选择器、颜色模式控件以及搜索。小屏幕上,溢出的主菜单项仍可通过横向滚动访问。

默认外观

导航栏使用本地品牌配色和固定的最小高度。

移动端

品牌与操作控件保持可见,主菜单可以滚动。应测试较长的中文标签、200% 缩放、触控目标、焦点顺序以及两种页面方向。

桌面端

主菜单在一行内展开;版本、语言、模式和搜索控件保持分组。不要添加过多自定义入口,以免把控件挤出视口。

覆盖图上的默认半透明效果

blocks/cover 短代码会把导航栏标记为覆盖图感知状态。导航栏起初为半透明,页面滚动后恢复常规背景。

行为通过配置调整,表现通过项目 SCSS 调整。覆盖导航栏 Partial 时,必须保留导航地标、焦点顺序、无障碍标签和响应式溢出行为。

在主题样式编译前覆盖 $td-navbar-min-height。锚点偏移、侧边栏高度、移动端换行和覆盖图都依赖该值,因此必须重新测试。

在两种模式下分别设置 --td-navbar-bg-color--td-brand-header-bg。背景为半透明时,应在所有覆盖图上验证对比度,并为滚动状态提供不透明背景。

覆盖图需要浅色前景控件时,页面可以在 Front Matter 或 cascade 中设置 ui.navbar_theme: dark。这只会调整导航栏组件样式,不会强制改变整个站点的颜色模式。

自定义覆盖图上的半透明效果

可以在站点级禁用半透明:

params:
  ui:
    navbar_translucent_over_cover_disable: true

覆盖图不可预测,或无障碍审查无法保证对比度时,应优先关闭该效果。

设置项目徽标与名称样式

徽标 Partial 覆盖项放在 layouts/partials/,源资源放在 assets/static/。具有信息含义的标志应提供有意义的替代文本;纯装饰标志应使用空替代文本。SVG 必须包含 view box,并为两种模式继承或定义颜色。

OINK 样例使用带本地渐变效果的文字标识。站点标题在语言配置中修改,视觉变量在项目 SCSS 中修改;可选择的文字能够胜任时,不要用图片替代品牌名称。

浅色/深色模式菜单

params.ui.showLightDarkModeMenu 为 true 时显示选择器。应把它留在共享导航中,使颜色状态在所有语言和页面类型中保持一致。

告警

Markdown 告警类型会映射到语义化 OINK/Bootstrap 样式。.alert-* 与告警渲染钩子应成对调整,保留可见标签或图标,并测试每种背景中的链接和行内代码。语法参见添加内容

表格

Markdown 表格具有响应式和主题感知样式。单元格应保持简洁,表头应使用真正的标题单元格;需要上下文时可在自定义 HTML 中添加标题,并在移动端测试横向溢出。不能用表格布局互不相关的内容。

自定义模板

Hugo 会优先解析站点布局,再解析主题布局。只复制确实需要修改的最小 Partial,并在同步上游时进行对比;覆盖完整 baseof.html 可能会悄然遗漏后续的无障碍与资源流水线修复。

在 head 或 body 末尾添加代码

Head 附加内容使用 layouts/partials/hooks/head-end.html,脚本或结束集成使用 layouts/partials/hooks/body-end.html。资源应自行托管,只在需要的页面加载,并与生产 CSP 保持兼容。

在页面正文前添加横幅

根据页面参数设置条件,并覆盖相应钩子或内容 Partial。横幅不得遮挡页面标题、困住键盘焦点,也不能把锚点目标挤到固定导航下方。

为 body 元素添加自定义 class

在页面 Front Matter 或分区 cascade 中设置 body_class

---
body_class: product-reference
---

OINK 会把该值追加到自动生成的 body class。请使用项目专属且有语义的名称,绝不能向该字段写入不可信内容。

12 - 文档版本管理

为多个文档版本自定义导航与提示横幅。

根据项目的发布和版本管理方式,你可能需要让用户访问旧版文档。旧版本的具体部署方式由你决定。本页介绍 OINK 提供的功能:在各个文档版本之间导航,并在归档站点上显示信息横幅。

添加版本下拉菜单

如果在 hugo.tomlhugo.yamlhugo.json 中添加 [params.versions],OINK 会在顶部导航栏加入版本下拉选择器。请为每个需要加入菜单的版本指定 URL 和名称,例如:

# Add your release versions here
[[params.versions]]
  version = "master"
  url = "https://master.kubeflow.org"

[[params.versions]]
  version = "v0.2"
  url = "https://v0-2.kubeflow.org"

[[params.versions]]
  version = "v0.3"
  url = "https://v0-3.kubeflow.org"
params:
  versions:
    - version: master
      url: 'https://master.kubeflow.org'
    - version: v0.2
      url: 'https://v0-2.kubeflow.org'
    - version: v0.3
      url: 'https://v0-3.kubeflow.org'
{
  "params": {
    "versions": [
      {
        "version": "master",
        "url": "https://master.kubeflow.org"
      },
      {
        "version": "v0.2",
        "url": "https://v0-2.kubeflow.org"
      },
      {
        "version": "v0.3",
        "url": "https://v0-3.kubeflow.org"
      }
    ]
  }
}

别忘了加入当前版本,这样用户才能返回!

版本下拉菜单的默认标题是 Releases。要修改标题,请在 hugo.tomlhugo.yamlhugo.json 中调整站点参数 version_menu

[params]
version_menu = "Releases"
params:
  version_menu: Releases
{
  "params": {
    "version_menu": "Releases"
  }
}

如果把 version_menu_pagelinks 参数设为 true,版本下拉菜单会链接到其他版本中的当前页面,而不是它们的首页。如果文档在不同版本之间变化不大,这项功能会很有用。请注意:如果当前页面在另一版本中不存在,链接就会失效。

还可以分别配置每个菜单项:

  • 如果菜单标签不是版本号,使用 name 代替 version
  • name 设为 --- 可添加菜单分隔线。
  • 省略 url 可渲染禁用的文本项,例如分组标题。
  • 设置 kind 可添加与类型对应的 CSS 类。详情请参阅导航与菜单
  • 即使全局 version_menu_pagelinks 参数为 true,仍可在某个菜单项上设置 pagelinks: false,让它始终链接到该版本首页。

例如:

params:
  version_menu: v1.2
  version_menu_pagelinks: true
  versions:
    - name: '**Versions**'
    - version: v1.3-dev
      kind: next
      url: https://next.example.com
    - version: v1.2
      kind: latest
      url: https://docs.example.com
    - name: ---
    - name: Preview variant
      kind: home
      pagelinks: false
      url: https://preview.example.com

要进一步了解 OINK 菜单,请参阅导航与菜单

在归档文档站点显示横幅

如果为旧版文档创建归档快照,可以在归档文档的每个页面顶部添加提示,告诉读者他们正在查看不再维护的快照,并提供指向最新版本的链接。

例如,可以查看 Kubeflow v0.6 归档文档

一个文本框,说明当前页面是不再维护的文档快照。
图 1:Kubeflow v0.6 归档文档中的横幅

要在文档站点加入横幅,请在 hugo.tomlhugo.yamlhugo.json 中完成以下修改:

  1. 将站点参数 archived_version 设为 true

    [params]
    archived_version = true
    params:
      archived_version: true
    {
      "params": {
        "archived_version": true
      }
    }
  2. 将站点参数 version 设为归档文档集的版本。例如,如果归档文档对应 0.1 版:

    [params]
    version = "0.1"
    params:
      version: 0.1
    {
      "params": {
        "version": "0.1"
      }
    }
  3. 确认站点参数 url_latest_version 包含希望读者前往的网站 URL。大多数情况下,它应该是最新版文档的 URL:

    [params]
    url_latest_version = "https://your-latest-doc-site.com"
    params:
      url_latest_version: https://your-latest-doc-site.com
    {
      "params": {
        "url_latest_version": "https://your-latest-doc-site.com"
      }
    }

13 - AI 智能体支持

帮助 AI 智能体和自动化工具发现并使用站点内容的可选功能,包括 Markdown 输出、HTML 中的备用链接与 llms.txt。

功能

站点显式启用后,OINK 会提供以下面向用户和机器可读的行为:

  • 支持 Markdown 输出格式。项目的 outputs 配置决定哪些页面类型发布 Markdown。
  • 发现机制:页面 HTML 的 header 会包含指向该页 Markdown 版本的 rel="alternate" 链接。
  • 查看 Markdown:页面元信息区域会显示指向 Markdown 版本的“查看 Markdown”链接。
  • llms.txt:位于站点根目录的内容清单文件。

本页其余部分介绍如何启用各项功能,并结合示例讨论相应的验证与指标

启用 Markdown 输出

Hugo 提供多种内置输出格式,其中包括 markdown。要启用 Markdown 输出,请在 Hugo 的 outputs 配置中,把 markdown 加入需要支持的页面类型。例如:

outputs:
  home: [HTML, markdown]
  page: [HTML, markdown]
  section: [HTML, RSS, print, markdown]
[outputs]
home = [ "HTML", "markdown" ]
page = [ "HTML", "markdown" ]
section = [ "HTML", "RSS", "print", "markdown" ]
{
  "outputs": {
    "home": ["HTML", "markdown"],
    "page": ["HTML", "markdown"],
    "section": ["HTML", "RSS", "print", "markdown"]
  }
}

让页面退出 Markdown 输出

如果要让某些页面不输出 Markdown,请在页面 front matter 中把 outputs 设为仅 HTML,或者在排除 markdown 的同时列出该页原本的全部默认输出格式。例如:

---
title: HTML-only test page
outputs: [HTML]
---
...

启用 llms.txt

llms.txt 是一种简单的文本格式,用来列出指向站点机器可读内容的链接。智能体可以轻松发现和解析它,它也能补充信息更丰富但结构更复杂的 Markdown 输出。进一步了解请参阅 llmstxt.org

OINK 会在站点根目录生成 llms.txt,其中包含首页、主菜单页面,以及存在时的 Markdown 备用版本链接。要启用它,请在 Hugo 的 outputs 配置中为首页添加 LLMS。例如:

outputs:
  home: [HTML, markdown, LLMS]
  page: [HTML, markdown]
  section: [HTML, RSS, print, markdown]

本站生成的 llms.txt 示例请参阅 /llms.txt

自定义输出

OINK 通过 layouts/all.md 渲染 Markdown 输出,并通过 layouts/index.llms.txt 生成 llms.txt。你可以在多个层级覆盖默认行为:

  • 按类型:在项目的 layouts/ 下添加 home.md_default/single.md 等模板,为特定 Hugo 类型定制 Markdown 输出。
  • 按短代码:为项目本地短代码添加输出格式专属短代码模板,使其在适当场景输出便于 Markdown 使用的内容。
  • 按页面:为需要精心设计智能体视图的高价值页面提供专属内容或结构。

服务端支持

虽然不属于 OINK 的支持范围,站点仍可通过服务端内容协商,帮助智能体发现和访问 Markdown 内容。例如,在与 HTML 相同的 URL 上响应 Accept: text/markdown

验证与指标

我们使用 AFDocs 评估面向智能体内容的基础结构支持,并验证生成的输出是否满足配置的检查项。我们也鼓励站点针对智能体访问模式实现自己的监控和指标,例如记录对 Markdown URL 或 llms.txt 的请求,并统计其使用情况。详情请参阅智能体支持检查

oink.pgsty.com 项目包含 AFDocs 配置和 npm 脚本,维护者可据此对已部署 URL 评分。这些检查与 OINK 的智能体支持目标有重合,包括 Markdown URL、llms.txt 和相关类别。

评分表示例

评分表示例包括:

  • OpenTelemetry 智能体评分在线报告;

  • 本站的 AFDocs 评分表:

    oink.pgsty.com 评分表

    Running in oink.pgsty.com…

    Agent-Friendly Docs Scorecard

    http://localhost:1313 · 4/26/2026, 5:43:59 AM

    Overall Score: 100 / 100 (A+)

    Category Scores: Content Discoverability 100 / 100 (A+) Markdown Availability 100 / 100 (A+) Page Size and Truncation Risk 100 / 100 (A+) Content Structure 100 / 100 (A+) URL Stability and Redirects 100 / 100 (A+) Observability and Content Health 100 / 100 (A+) Authentication and Access 100 / 100 (A+)

    Check Results:

    Content Discoverability
        PASS  llms-txt-exists                llms.txt found at 1 location(s)
        PASS  llms-txt-valid                 llms.txt follows the proposed structure (H1, blockquote, heading-delimited link sections)
        PASS  llms-txt-size                  llms.txt is 1,131 characters (under 50,000 threshold)
        PASS  llms-txt-links-resolve         All 13 same-origin links resolve (13 total links)
        PASS  llms-txt-links-markdown        13/13 same-origin links point to markdown content (100%)
        PASS  llms-txt-directive             llms.txt directive found in all 13 pages, near the top of content
      
      Markdown Availability
        PASS  markdown-url-support           13/13 pages support .md URLs (100%)
        PASS  content-negotiation            13/13 pages support content negotiation (100%)
      
      Page Size and Truncation Risk
        PASS  rendering-strategy             All 13 pages contain server-rendered content
        PASS  page-size-markdown             All 13 pages under 50K chars (median 2K, max 9K)
        PASS  page-size-html                 All 13 pages convert under 50K chars (median 2K, 0% boilerplate)
      
      Content Structure
        PASS  tabbed-content-serialization   No tabbed content detected across 13 pages
        PASS  section-header-quality         No tabbed content found; header quality check not applicable
        PASS  markdown-code-fence-validity   All 1 code fences properly closed across 14 pages
      
      URL Stability and Redirects
        PASS  http-status-codes              All 13 pages return proper error codes for bad URLs
        PASS  redirect-behavior              No redirects detected across 13 pages
      
      Observability and Content Health
        PASS  cache-header-hygiene           All 14 endpoints have appropriate cache headers
      
      Authentication and Access
        PASS  auth-gate-detection            All 13 pages are publicly accessible
        SKIP  auth-alternative-access        All docs pages are publicly accessible; no alternative access paths needed
      

    Full spec: https://agentdocsspec.com/spec/

这些检查的配置详情请参阅智能体支持检查


  1. 这与 Hugo 文档描述的 front matter 配置行为不同,但截至 Hugo 0.158.0,我们的测试确认实际行为如此。 ↩︎