外观与风格

定制 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。请使用项目专属且有语义的名称,绝不能向该字段写入不可信内容。