这是本节的多页打印视图。 .

返回本页常规视图.

定制外观

字体预设、配色与样式覆盖。

OINK 的外观定制建立在语义化 CSS 自定义属性之上:站点覆盖少量变量即可换肤,不需要复制主题的组件选择器。

本章内容

定制的优先级

从低到高:

  1. 主题默认值
  2. 预设(typography.preset
  3. _variables_project.scss 里的 Sass 变量
  4. _styles_project.scss 里的自定义属性覆盖

站点设置永远优先于预设默认值。

1 - 字体预设

用语义化字体角色换字体,不必复制主题的组件选择器。

OINK 把字体选择收敛到七个语义化 CSS 自定义属性之后。站点覆盖这些角色即可换字体,不需要知道主题内部用了哪些选择器。

整套机制由 Hugo 在构建期编译进同一份静态样式表:不引入 JavaScript、不请求远程字体服务、不需要运行时预设加载器

两个内置预设

hugo.yaml
YAML
params:
  ui:
    typography:
      preset: technical # technical | system
预设 效果
technical 默认值。保留 Chakra Petch 与 IBM Plex Mono 的工程观感,字体文件全部本地打包
system 展示、元信息、打印与等宽角色改用平台字体栈,不请求任何 OINK 品牌字体

选中的值会输出到 <html>data-td-typography 属性上。无效取值直接让构建失败,而不是静默改变站点外观。

七个语义角色

--td-ui-font-family , CSS 属性

导航、控件与界面外壳。默认继承 --bs-body-font-family

--td-body-font-family , CSS 属性

文章与博客正文。默认继承 UI 角色。

--td-heading-font-family , CSS 属性

内容标题。默认取 $headings-font-family,未设置时回落到正文角色。

--td-code-font-family , CSS 属性

代码与终端内容。默认取 $font-family-code

--td-display-font-family , CSS 属性

文字商标与展示型标题。默认 Chakra Petch,然后回落到 UI 角色。

--td-meta-font-family , CSS 属性

技术标签与元信息。默认 IBM Plex Mono,然后回落到代码角色。

--td-print-font-family , CSS 属性

仅用于打印输出的正文。

主题组件只消费这些角色或组件级别名(例如 --td-asciinema-font-family),不会直接写死字体名。依赖方向始终是:

TEXT
Bootstrap 基础变量  →  OINK 语义角色  →  组件别名

反向引用是禁止的——Bootstrap 的自定义属性不会引用 OINK 的属性,这样依赖图保持单向,不会产生自定义属性循环。

使用站点自己的字体

把本地 .woff2 放到站点的 static/webfonts/ 下,在 _styles_project.scss 里声明字体并覆盖需要的角色。Hugo会在主题样式之后加载这个文件:

assets/scss/_styles_project.scss
SCSS
@font-face {
  font-family: 'My Sans';
  font-display: swap;
  font-style: normal;
  font-weight: 400 800;
  src: url('../webfonts/my-sans-variable.woff2') format('woff2');
}

:root {
  --td-ui-font-family: 'My Sans', 'Noto Sans SC', sans-serif;
  --td-body-font-family: var(--td-ui-font-family);
  --td-heading-font-family: var(--td-ui-font-family);
  --td-display-font-family: var(--td-heading-font-family);
}

远程 URL 和任意 CSS 不能通过 YAML 注入。 字体文件和样式表始终是本地的、可审查的构建输入。

按内容类型分别设置

角色是正常继承的,所以给某一类内容单独换字体不需要新增全局预设,也不用复制组件选择器:

assets/scss/_styles_project.scss
SCSS
body.td-blog {
  --td-body-font-family: 'My Serif', 'Noto Serif SC', serif;
  --td-heading-font-family: var(--td-body-font-family);
}

与旧配置的兼容

已有的 Sass 定制仍然是这套系统的第一输入。OINK 复用既有变量而不是另起一套:

既有变量 OINK 的解读
$font-family-base / $font-family-sans-serif Bootstrap 正文字体,进而作为 UI 与正文角色
$headings-font-family 显式配置时作为标题角色
$font-family-monospace Bootstrap 等宽基线
$font-family-code 代码角色,以及普通 codeprekbdsamp
$td-google-font-name 默认打印字体

这些覆盖写在 assets/scss/_variables_project.scss,与 Docsy 一致,会被编译进角色默认值。

_styles_project.scss 里的自定义属性覆盖在更晚的层级生效,因此仍可用于上下文相关的定制。项目设置优先于预设默认值是有意为之。

范围说明

这是设计令牌工作的第一片:只覆盖字体。语义颜色、表面、圆角、密度和外观预设不在其中——它们应该在各自的 Bootstrap与壳层令牌契约有了对应的回归覆盖之后再单独引入。

下一步

2 - 外观与风格

定制主题、字体、代码样式与页面布局。

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 在主题组件样式之后加载项目选择器

先从最小覆盖项开始:

SCSS
// assets/scss/_variables_project.scss
$primary: #315f8f;
$secondary: #b4762e;
SCSS
// assets/scss/_styles_project.scss
body.td-blog {
  --td-body-font-family: 'Noto Serif', 'Noto Serif SC', serif;
}

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

高级样式定制

稳定的定制层次、字体 preset、语义字体角色与按内容限定作用域的完整说明,参见高级定制

OINK 的 SCSS 导入顺序如下:

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

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

⚠️ 重置内部样式

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

额外样式

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

GO-HTML-TEMPLATE
{{ $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 变量:

SCSS
$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'] 中成对覆盖:

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

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

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

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

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

浅色/深色模式

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

YAML
params:
  ui:
    showLightDarkModeMenu: true

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

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

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

禁用深色模式

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

YAML
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 中设置字体:

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;

OINK 还提供构建时字体 preset 与不依赖运行时脚本的语义字体角色。博客、OpenAPI 或代码密集页面的作用域示例与完整公开接口,参见高级定制

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

CSS 工具类

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

代码块

OINK 默认支持 Hugo Chroma,并提供本地纳管的 Prism 兼容选项。一个站点应统一选择一种高亮器;同时启用会产生重复标记或样式。文件名、复制策略、换行、折叠、行锚点与可分享代码组的完整说明参见代码块与代码组

使用 Chroma 进行代码高亮

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

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

Chroma 基础样式配置

在 Hugo 中配置标记渲染:

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

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

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

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

选择控制台代码块内容

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

未指定语言的代码块

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

复制到剪贴板

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

使用 Prism 进行代码高亮

设置:

YAML
params:
  prism_syntax_highlighting: true

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

没有语言的代码块

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

扩展 Prism 语言或插件

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

OINK 导航栏包含项目标识、主菜单、按需显示的版本与语言选择器、颜色模式控件、搜索以及仓库链接。除非页面用 navbar_enabled 关闭,它会在所有布局上渲染。

默认外观

导航栏使用本地品牌配色和固定的最小高度,并且只有两种状态,而不是桌面布局加一套独立的移动菜单。

移动端

小于 lg 时,导航栏保留 Logo,其余条目全部变成右对齐的图标,没有任何内容藏进汉堡按钮。小于 md 时,带外壳的页面会再多出一个用于打开侧边栏抽屉的图标。应测试较长的中文标签、200% 缩放、触控目标、焦点顺序以及两种页面方向;同时为每个顶层菜单项配置 pre 图标,因为在这个宽度下只有图标会保留下来。

桌面端

lg 及以上时,主菜单连同文字标签在一行内展开;版本、语言、模式、搜索与仓库控件成组排在末尾。父级菜单项在悬停或键盘聚焦时展开面板,点击则直接跳转。不要添加过多自定义入口,以免把控件挤出视口。

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

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

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

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

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

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

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

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

YAML
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

YAML
---
body_class: product-reference
---

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

3 - 高级定制

通过兼容 Docsy 的 Sass 输入与 OINK 语义 token 定制字体和局部视觉样式。

OINK 提供一套小而清晰的分层定制接口,站点不需要复制组件选择器,也不需要分叉主题。全部样式仍由 Hugo Extended 与 Hugo Pipes 处理,不引入 Node.js、npm、PostCSS、远程字体服务或浏览器端 preset 加载器。

本文记录正式支持的扩展点,以及第一组公开的语义 token:字体系统。颜色模式、代码高亮、导航栏与模板定制示例,参见外观与风格

选择正确的定制层

优先使用能够表达需求的最高层接口:

层级 扩展点 适用场景
Hugo 配置 hugo.yaml 或页面 Front Matter 字体 preset、页面宽度等正式支持的选项
Sass 基础层 assets/scss/_variables_project.scss 影响整个样式包的 Docsy 与 Bootstrap 既有变量
Bootstrap 后置 Sass assets/scss/_variables_project_after_bs.scss 少数依赖 Bootstrap 变量或 map 的覆盖项
语义 CSS assets/scss/_styles_project.scss 语义角色、某类内容、某组页面或单个组件
Hugo 模板 layouts/ 与 Partial hook CSS 无法表达的结构或 DOM 修改

先尝试配置项或既有 Sass 变量;需要缩小作用域时,再使用语义 CSS 属性。只有找不到合适 token 时才覆盖选择器,只有结构本身必须变化时才覆盖模板。

站点品牌定制不要直接修改主题、Bootstrap、Font Awesome 或主题字体目录中的文件。这些修改难以审计,也会在升级时丢失或产生冲突。

CSS 接口如何分层

OINK 保持单向依赖:

TEXT
Docsy / Bootstrap Sass 变量
Bootstrap --bs-* 属性
OINK 语义 --td-* 角色
组件别名与选择器

Docsy 与 Bootstrap 的既有变量继续充当基础层。只有多个组件需要共享同一种含义时,OINK 才增加语义角色,例如“文章正文”或“技术元信息”。Bootstrap 属性不会反向引用 OINK 角色,因此不会形成 CSS 自定义属性循环。

接口稳定性有明确边界:

  • 在可行范围内,既有 Docsy 与 Bootstrap 变量继续作为兼容输入;
  • 下文列出的字体角色是面向站点定制的公开 API;
  • --td-asciinema-font-family 这类已记录的组件别名只承诺更窄的组件级用途;
  • 未记录的 --td-shell-* 与选择器局部属性属于实现细节。不能因为某个属性以 --td-* 开头,就默认它是公共 API。

字体 preset

hugo.yaml 中选择全站内置 preset:

YAML
params:
  ui:
    typography:
      preset: technical # technical | system
Preset 效果 字体请求
technical 默认 OINK 风格,包括 Chakra Petch 展示文字与 IBM Plex Mono 技术文字 只使用主题本地文件
system 使用操作系统 sans 与 monospace 字体栈,适合作为中性基础,也能缩减文本字体请求 除非项目 CSS 主动引用,否则不会请求 OINK 自带的文本字体

OINK 会把最终取值写入 <html> 元素的 data-td-typography 属性。无效值会让 Hugo 构建失败,而不是静默回退。这是站点构建时选择,不是依赖 JavaScript 的读者偏好设置。

Preset 只提供默认值,项目 Sass 与 CSS 始终拥有最终控制权。例如,站点即使选择了 system,只要项目样式明确引用 IBM Plex Mono,浏览器仍会加载它。

公开字体角色

组件只消费语义角色,不直接指定品牌字体:

CSS 属性 控制范围 默认来源
--td-ui-font-family 导航、控件、搜索与通用界面外壳 Bootstrap 正文字体
--td-body-font-family 文档与博客正文 UI 角色
--td-heading-font-family 文章标题 $headings-font-family,未设置时使用正文角色
--td-code-font-family codeprekbdsamp 与终端内容 $font-family-code
--td-display-font-family 字标与展示型标题 Chakra Petch,后备为 UI 角色
--td-meta-font-family 技术标签与元信息 IBM Plex Mono,后备为代码角色
--td-print-font-family 打印正文及默认打印标题 $td-google-font-name,后备为 Bootstrap 正文字体

大多数情况下应修改较宽泛的语义角色。例如 Asciinema 使用 --td-asciinema-font-family,其默认值是 --td-code-font-family。只有终端回放确实需要与其他代码不同的字体时,才单独覆盖组件别名。

复用 Docsy 与 Bootstrap Sass 变量

OINK 直接解释既有名字,不再增加一组平行的 Sass 开关:

既有变量 OINK 的解释方式
$td-fonts-serif$font-family-sans-serif$font-family-base Bootstrap 正文字体,继而成为 UI 与正文角色
$headings-font-family 明确设置时成为标题角色
$td-font-family-monospace$font-family-monospace Bootstrap 等宽字体基础
$font-family-code 代码角色与普通代码元素
$td-google-font-name 默认打印字体

与 Docsy 一样,把这些编译期覆盖项写入 _variables_project.scss

SCSS
// assets/scss/_variables_project.scss
$font-family-sans-serif: 'Noto Sans SC', 'PingFang SC', system-ui, sans-serif;
$headings-font-family: $font-family-sans-serif;
$font-family-monospace:
  'Sarasa Mono SC', 'Cascadia Code', ui-monospace, monospace;
$font-family-code: $font-family-monospace;

这会修改编译后的默认值。同一站点的不同区域需要不同风格时,请在 _styles_project.scss 中覆盖语义 CSS 属性。

添加站点自有字体

把经过审查的 .woff2 文件放在使用方站点的 static/webfonts/ 目录中,再到 _styles_project.scss 声明并分配字体:

SCSS
// assets/scss/_styles_project.scss
@font-face {
  font-family: 'My Sans';
  font-display: swap;
  font-style: normal;
  font-weight: 400 800;
  src: url('../webfonts/my-sans-variable.woff2') format('woff2');
}

:root {
  --td-ui-font-family:
    'My Sans', 'Noto Sans SC', 'PingFang SC', system-ui, sans-serif;
  --td-body-font-family: var(--td-ui-font-family);
  --td-heading-font-family: var(--td-ui-font-family);
  --td-display-font-family: var(--td-heading-font-family);
}

内容可能包含中文时,应为代码字体明确提供 CJK 后备:

SCSS
:root {
  --td-code-font-family:
    'My Mono', 'Sarasa Mono SC', 'Noto Sans Mono CJK SC', monospace;
}

应对子集进行裁剪并自行托管字体,包含站点需要的全部文字,设置 font-display: swap,并记录许可证。OINK 有意不允许通过 YAML 注入任意字体 URL 或 CSS 字符串。

按内容类型限定样式

语义属性可以正常继承,因此内容专属风格不需要第二份样式表,也不需要复制组件规则。博客页面已经带有 td-blog body class:

SCSS
// assets/scss/_styles_project.scss
body.td-blog {
  --td-body-font-family: 'My Serif', 'Noto Serif SC', serif;
  --td-heading-font-family: var(--td-body-font-family);
}

OINK 还会为 Swagger/OpenAPI 页面添加 td-swagger。如果是项目自己定义的页面类型,可在 Front Matter 或 section cascade 中设置 body_class

YAML
---
body_class: code-reference
page_width: wide
---
SCSS
body.code-reference {
  --td-meta-font-family: var(--td-code-font-family);
}

请使用站点自有且具有语义的 class 名称,绝不能向 body_class 写入不可信内容。

让布局与字体彼此独立

字体、文章宽度与组件结构是三个独立维度。OINK 现有的 page_width 参数支持 normalwidefull,既可全局设置,也可在页面 Front Matter 中覆盖:

YAML
params:
  page_width: normal

这样无需制造一个包办一切的 preset,就能组合出不同使用体验:

使用场景 推荐组合
标准文档 page_width: normal 加全站字体 preset
全宽画布 page_width: full,必要时再加页面专属 body_class
博客或文章阅读 td-blog 中覆盖正文与标题角色,通常保持 normal 宽度
代码密集型参考页 用项目 body class 调整代码与元信息角色
OpenAPI 参考页 使用 Swagger layout 与 td-swagger,渲染器专属结构留在对应布局中

将这些关注点分开,可以避免 preset 数量膨胀,后续 editorial、API 或代码风格也能复用同一组语义角色。

颜色与组件 surface

编译期配色应使用 $primary$secondary$danger 等 Bootstrap 变量;运行时则优先使用 --bs-body-bg--bs-body-color--bs-link-color--bs-border-color 等 Bootstrap 语义属性。

OINK 还记录了一小组品牌层属性,包括 --td-brand-elev--td-brand-silk--td-brand-copper--td-brand-header-bg--td-brand-mark-gradient。浅色与深色值应成对覆盖,完整示例参见外观与风格

不要仅仅因为某个 shell 或组件 token 的名字看起来顺手,就在全局覆盖它。应先修改它的 Bootstrap 来源或已记录的语义来源;只有该组件确实需要独立变化时,才使用组件别名。

验收清单

发布定制样式前:

  1. 使用当前支持的最旧与最新 Hugo Extended 版本构建;
  2. 检查浅色、深色、打印、forced-colors 与 reduced-motion;
  3. 检查涉及范围内的文档、博客、代码、搜索与 OpenAPI 页面;
  4. 检查窄屏和宽屏,以及较长的 CJK 文本与代码行;
  5. 确认字体请求全部来自本地,加载有意、许可证清楚,而且体积没有不必要的膨胀;
  6. 优先使用一次语义覆盖,避免重复堆叠选择器补丁。

这些约束共同守住 OINK 的核心前提:定制之后,站点仍然只依赖一个 Hugo 二进制文件,不增加额外构建或运行时依赖。