1 - Adding Content

Structure and author bilingual documentation and blog content in OINK.

OINK uses Hugo’s content model: Markdown carries the information, front matter carries page metadata, and layouts turn both into a static site. This guide describes the conventions used by the bundled English and Simplified Chinese sample site.

Content root directory

Site content lives below content/. A multilingual site can use separate roots such as content/en/ and content/zh/, or translated filename suffixes in one mounted tree. This repository uses the second form:

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

The English file is the source page and the .zh.md file is its Simplified Chinese translation. Both files share the same logical path after Hugo applies the language suffix.

Keep generated files and files that must be copied byte-for-byte outside the content tree. Put those in static/ as described in Adding static content.

Content sections and templates

Every top-level content directory is a Hugo section. OINK includes layouts for:

  • docs: documentation with a section tree, table of contents, breadcrumbs, previous/next navigation, and repository links;
  • blog: dated articles, taxonomy metadata, feeds, and chronological lists;
  • community: project and contributor links;
  • default pages: landing pages without the documentation sidebar.

Hugo chooses a layout from the content section. A page below content/docs/ therefore uses the docs layout. Set type in front matter only when a page must use another section’s layout.

Custom sections

Create a directory below the content root, then give its pages a type when the default layout is not sufficient:

---
title: Architecture decisions
description: Accepted design decisions for the project.
type: docs
weight: 30
---

For section-wide behavior, put shared values in the section’s _index.md cascade rather than repeating them on every page. Add a project layout under layouts/ only when no existing OINK layout or partial is suitable.

Doc-rooted sites

EXPERIMENTAL

A documentation-first site can publish the docs section at the URL root while keeping source files under content/.../docs/:

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

The docs section landing page then becomes the home page. Add this front matter to the physical site-root index for each language so it can still act as a link without competing for the same output path:

build: { render: link }

Check for path conflicts

Docs now share the URL root with blog, community, and other sections. Build with --printPathWarnings and resolve every duplicate target before publishing:

hugo --printPathWarnings

Legacy docs-only setup

Older Docsy examples used a front matter cascade to force page types. Remove that workaround when moving to the permalink-based doc-rooted setup; otherwise the home page and section layouts can resolve inconsistently.

Page front matter

Front matter is page metadata written in YAML, TOML, or JSON. OINK’s sample site uses 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 is the practical minimum. In maintained documentation, also provide a concise description for search and metadata, and a weight when order matters. Use linkTitle only when navigation needs a shorter label.

Translations should localize human-facing metadata while preserving structural values:

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

Do not translate keys, shortcode names, configuration keys, file paths, or stable identifiers.

Docs and blog pages render a compact metadata block above the site footer. The last-modified date comes from Hugo’s .Lastmod value. Two optional front matter fields add provenance notices:

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

upstream_attribution links to the upstream source and its attribution. downstream_modified: true states that the downstream project changed the page. Omit either field when its notice does not apply.

Page content

Write pages in Markdown unless a layout genuinely requires HTML. Hugo renders Markdown with Goldmark and supports attributes, footnotes, tables, task lists, render hooks, and fenced code blocks.

Markdown

Keep source readable without the rendered site:

  • use ATX headings (## Heading);
  • put blank lines around lists, blocks, and fenced code;
  • specify the language of every code fence when one exists;
  • use descriptive link text and image alternative text;
  • wrap prose at a review-friendly width, but never reflow code or URLs.

OINK adds render hooks for blockquote alerts and for Mermaid, math, chemistry, Markmap, and PlantUML code blocks. See Diagrams and Formulae.

Markup, shortcodes, and content features

Use standard Markdown for ordinary prose. Use a shortcode when it supplies meaningful behavior such as tabs, cards, a terminal recording, an API viewer, or a safe chart. Shortcodes are part of the content contract: verify their arguments in both languages and avoid copying rendered HTML into translations.

Alerts

OINK supports GitHub-style blockquote alerts and optional Obsidian-style titles:

> [!TIP]
>
> Run the translation audit before every release.

> [!WARNING] Stable anchors required
>
> A translated heading must keep the English page's rendered ID.

Supported semantic types include NOTE, TIP, IMPORTANT, WARNING, and CAUTION, plus the Bootstrap-compatible types and NB. Use alerts sparingly: important instructions must still make sense to screen readers and in print. See Alerts for appearance.

Use root-relative links for stable public routes and ordinary relative links for nearby pages or bundle resources. Hugo’s ref and relref shortcodes validate content references and account for language and permalink rules:

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

For bilingual pages:

  • link to the logical page, not directly to a .zh.md filename;
  • keep fragment IDs language-neutral;
  • verify that both language variants resolve the same fragment;
  • use relref when the destination must remain relative to the current host.

Run the internal-link check after changing routes or headings.

Content style

Write task-oriented documentation in direct language. Introduce a concept before its configuration, state defaults explicitly, and distinguish local build verification from deployment or publication. The Chinese edition follows the terminology and typography rules in oink.pgsty.com/TRANSLATION.md.

Page bundles

A standalone page is a single Markdown file. A leaf bundle is a directory with an index.md and page resources:

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

Both language pages can use the same image and downloadable file. Hugo normally shares page resources across language variants on a single host, so do not duplicate identical binary assets. Localize an image only when it contains meaningful text; give the localized resource a clear language suffix.

Use branch bundles (_index.md) for sections that contain child pages and leaf bundles (index.md) for terminal pages with resources.

Adding docs and blog posts

Create every maintained English page and its Chinese peer in the same directory:

guide.md
guide.zh.md

For bundle pages, pair index.md with index.zh.md. Keep routing metadata, dates, weights, aliases, and resource declarations aligned unless a language-specific difference is intentional.

Organizing your documentation

Use directories to reflect the reader’s information architecture, not the implementation’s package tree. Each documentation subsection needs an _index.md and an _index.zh.md. Child pages appear in the sidebar ordered by weight, then by the configured fallback ordering.

Prefer a shallow hierarchy. Split a page when it serves a distinct task or audience; do not split merely to shorten a file. See Organizing Your Content.

Docs section landing pages

A docs _index.md renders child-page summaries by default. Use:

simple_list: true

to render a compact list, or:

no_list: true

to suppress the generated list. Give each language variant a localized title and description, and keep the structural option identical.

Organizing your blog posts

Blog posts can live directly below blog/ or in year/category directories. OINK uses dated directories and pairs each article:

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

A post normally supplies:

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

Keep the publication date and author identity consistent across translations. Translate the title, description, taxonomy labels, caption text, and body. Do not translate commit IDs, release tags, commands, or URLs.

Working with top-level landing pages

Default-layout pages are suitable for the home page, product overview, and other destinations that do not need the docs sidebar.

Customizing the example site pages

The bundled home page is content/_index.md with content/_index.zh.md as its translation. It uses the same local assets and theme pipeline as the rest of OINK. Change content and project assets in the site; do not edit vendored runtime files merely to alter branding.

Building your own landing pages

Compose landing pages from standard Markdown and blocks/* shortcodes. Keep essential information in text, make call-to-action links meaningful, and test the page at mobile and desktop widths in both languages.

Adding a community page

Create community/_index.md and community/_index.zh.md. The community layout uses params.links.user and params.links.developer:

params:
  links:
    user:
      - name: User forum
        url: https://community.example.org/
        icon: fa-solid fa-comments
        desc: Ask questions and share solutions
    developer:
      - name: GitHub
        url: https://github.com/pgsty/oink
        icon: fa-brands fa-github
        desc: Source, issues, and pull requests

Entries may set rel; OINK also adds noopener to external HTTP links where appropriate. Set params.contributingUrl in the community page front matter if the contribution guide is not at the conventional docs route.

Adding static content

Files below static/ are copied to the published root without Markdown rendering or fingerprinting:

static/reference/api/index.html

is published as /reference/api/index.html. Use this for externally generated reference sites, verification files, and downloads that require stable names. Prefer page resources or Hugo Pipes for assets that need resizing, fingerprinting, or bundle-relative lookup.

OINK’s browser runtime is intentionally shipped from the theme or site itself. When adding a library, vendor and pin it, record it in theme/VENDOR.json, and do not introduce an implicit CDN fallback.

RSS feeds

Hugo creates feeds for the home page and list sections. Disable them globally only when the site has no feed consumers:

disableKinds: [RSS]

If a section declares custom outputs, retain RSS explicitly:

outputs:
  section: [HTML, RSS, print]

Check the generated language-specific feed URLs and ensure titles, summaries, dates, canonical URLs, and hreflang relationships are correct.

Sitemap

Hugo generates sitemap.xml by default. Site-wide settings are:

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

A page can override these values:

---
title: Release notes
sitemap:
  priority: 0.8
---

Treat changefreq and priority as hints, not promises. Exclude drafts, private material, and noncanonical duplicates before deployment, then inspect the generated sitemap for every published language.

2 - AI-agent support

Opt-in features that help AI agents and automated tools discover and use your site content, including Markdown output, alternate links in HTML, and llms.txt.

Features

When your site opts in, these are the user-facing and machine-readable behaviors Docsy enables:

  • Markdown output format support. Your project’s outputs configuration controls which page kinds publish Markdown.
  • Discovery: page HTML headers include rel="alternate" links to the Markdown version of the page.
  • View Markdown: page meta area includes a View Markdown link to the Markdown version of the page.
  • llms.txt: site-root file listing.

The remainder of this page explains how to enable each feature, and discusses validation and metrics supported with examples.

Enable Markdown output

Hugo comes with several built-in output formats, including markdown. To enable Markdown output, add markdown to the Hugo outputs configuration for the page kinds you want to support. For example:

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"]
  }
}

Opt pages out

To opt pages out of Markdown output, set outputs in page front matter to HTML only, or whatever your page’s default output formats are while excluding markdown. For example:

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

Enable llms.txt

The llms.txt format is a simple text format for listing machine-readable links to site content. It is designed to be easy for agents to discover and parse, and to complement the richer but more complex Markdown outputs. To learn more, see llmstxt.org.

Docsy generates llms.txt at the site root, and includes links to the home page, main menu pages, and Markdown alternates where they exist. To enable it, add LLMS to the Hugo outputs configuration for the home page. For example:

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

For an example of the generated llms.txt for this site, see /llms.txt.

Customize output

Docsy renders Markdown output via layouts/all.md and generates llms.txt via layouts/index.llms.txt. You can override these defaults at several levels:

  • Per kind — Add templates such as home.md or _default/single.md under layouts/ in your project to tailor Markdown output for specific Hugo kinds.
  • Per shortcode — Add output-format-specific shortcode templates to project-local shortcodes so they emit Markdown-friendly content when appropriate.
  • Per page — Provide page-specific content or structure for high-value pages that need a curated agent-facing view.

Server-side support

While outside the scope of Docsy’s support, sites can facilitate agent discovery and access to Markdown content by implementing server-side content negotiation. For example, honoring Accept: text/markdown on the same URL as HTML.

Validation and metrics

We use AFDocs to assess basic structural support for agent-facing content, and to validate that generated outputs meet the configured checks. We also encourage sites to implement their own monitoring and metrics on agent access patterns—for example logging requests to Markdown URLs or llms.txt, and collecting metrics on their use. For details, see Agent-support checks.

The oink.pgsty.com project contains AFDocs configuration and npm scripts so maintainers can score a deployed URL against checks that overlap with Docsy’s agent-support goals, including Markdown URLs, llms.txt, and related categories.

Scorecard examples

For scorecard examples, see:

  • OpenTelemetry agent score online report

  • An AFDocs scorecard for this site:

    oink.pgsty.com scorecard

    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/

For details on how these checks are configured, see Agent-support checks.


  1. This is contrary to the documented Hugo behavior for front-matter configuration, but it is confirmed with our testing as of Hugo 0.158.0. ↩︎

3 - Analytics, user feedback, and SEO

Configure optional analytics and feedback while keeping SEO metadata useful.

OINK does not contact analytics, form, comment, or advertising services by default. These integrations are site decisions: enable them explicitly, document the data boundary, and provide any consent or policy required by the site’s users and jurisdiction.

Adding analytics

Hugo provides embedded templates for analytics services. When a site configures Google Analytics, browser usage information such as page views and custom events is sent to Google. This is incompatible with a fully air-gapped runtime and may be incompatible with a strict same-origin Content Security Policy.

Setup

Obtain a Google Analytics measurement ID for the site, then use Hugo’s current service configuration:

services:
  googleAnalytics:
    id: G-YOUR-ID

Do not also set the deprecated top-level googleAnalytics key. Analytics are normally emitted only for a production Hugo environment. Build a production preview and inspect its HTML and browser network log before publication.

If analytics is disabled, OINK emits no Google Analytics request. Remove the configuration entirely rather than inserting a fake identifier.

User feedback

OINK can show a “Was this page helpful?” widget at the bottom of documentation pages. The widget presents Yes and No actions and then displays a configured response, usually with a link to open a documentation issue.

The page asks whether it was helpful and offers Yes and No buttons.
Figure 1. The page feedback widget

The response can remain useful without analytics: it can direct the reader to an issue template, discussion, email address, or another site-owned feedback channel. Collection and event reporting happen only when the site configures an appropriate destination.

How feedback data is useful

Combine feedback with context instead of treating one score as proof. Pages with high traffic and repeated negative feedback are useful review candidates; highly rated pages can reveal patterns worth testing elsewhere.

Make focused editorial changes when possible. For example, update one stale tutorial, or move a code example earlier on a small group of pages, then compare feedback over an appropriate period. Record releases, traffic shifts, support events, and other factors that could explain the change.

Feedback is directional evidence, not a substitute for user research, accessibility review, support data, or technical validation.

Setup

OINK keeps the widget off by default. Set the global default and configure localized responses. For English:

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>.

For Simplified Chinese, put translated strings in 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>。

Visible response HTML is trusted site configuration. Keep it small, review its links, and do not interpolate untrusted values.

When Google Analytics is configured, the widget can emit a custom page_helpful event. A positive action uses params.ui.feedback.max_value (100 by default); a negative action uses 0.

Access feedback data

For Google Analytics, inspect the page_helpful event in the provider’s events report and create a page-level report when needed. An absent event may mean no interaction occurred, analytics was blocked or disabled, consent was not given, or the selected time range is wrong.

Do not enable analytics solely to make the widget visible. A site can keep the response-and-link experience while leaving event collection disabled.

Override feedback on one page

Set feedback in page front matter. The page value overrides the global default in either direction:

---
title: Feedback example
feedback: true
---

Use feedback: false to hide the widget on a page when the global default is enabled. For compatibility, hide_feedback: true also hides it when feedback is not set.

Set the default for all pages

Set the site parameter. OINK defaults it to false; set it to true only when most documentation pages should show the widget:

params:
  ui:
    feedback:
      enable: false

Add a contact form with Fabform

Fabform and similar hosted form endpoints are optional online services. After creating an account and reviewing its data handling, a site can post a form to its assigned endpoint:

<form action="https://fabform.io/f/{form-id}" method="post">
  <label for="email">Your email</label>
  <input id="email" name="email" type="email" autocomplete="email" />
  <button type="submit">Submit</button>
</form>

Replace {form-id}, translate the visible labels, add a privacy notice, and provide error and success states. The form will not work offline. A local or first-party endpoint is preferable when the site must keep submissions within its own boundary.

Search engine optimization metadata

For each page, OINK chooses the HTML meta description from the first available value:

  1. the page’s description front matter field;
  2. Hugo’s computed page summary for non-index pages;
  3. the site description in params.

Write a concise, page-specific description in every language. Do not copy the English description into a Chinese page. Search metadata cannot compensate for thin, duplicated, or inaccurate content.

The theme also emits canonical and alternate-language links from Hugo’s page translations. Use a correct production baseURL, stable translated routes, and explicit translated heading IDs. Add other meta tags through the site’s layouts/_partials/hooks/head-end.html override only when they are not already provided by the theme.

See Hugo’s Google Analytics configuration, page summaries, and Google’s SEO starter guide for the underlying service and content concepts.

4 - Diagrams and formulae

Add local diagrams, mind maps, and scientific formulae to a page.

OINK supports KaTeX, Mermaid, Markmap, PlantUML, and Diagrams.net. KaTeX, Mermaid, and Markmap use build-time or same-origin resources shipped with the theme. PlantUML and the Diagrams.net editor require an explicitly configured service endpoint; they do not silently default to a public service.

LaTeX support with KaTeX

KaTeX renders TeX mathematics for the web. Hugo’s embedded KaTeX support can render formulae at build time, so readers do not need a remote math service.

Inline formulae

Inline formulae use the passthrough delimiter pairs configured in Goldmark. Keep surrounding spaces and punctuation outside the formula when possible.

Formulae in display mode

Use a math code block for a formula on its own line:

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

Activating KaTeX support

math and chem code blocks use theme render hooks automatically. For inline and delimiter-based formulae, enable Goldmark’s passthrough extension and set the delimiter pairs appropriate for the site. The included oink.pgsty.com config shows square-bracket, double-dollar, and parenthesis pairs.

Enable the passthrough extension

The relevant YAML structure is:

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

Fill the arrays with Hugo’s documented delimiter pairs. Choose pairs that do not conflict with the site’s prose or code and apply the setting consistently in every build environment.

Add the passthrough render hook

For delimiter-based math, create layouts/_markup/render-passthrough.html in the site:

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

The hook can be scoped to a content type or section by placing it under the corresponding layout directory. A scoped hook avoids treating unrelated content as mathematical passthrough.

Chemical equations and physical units

Hugo’s embedded KaTeX supports the mhchem extension. Use chem code blocks for chemical equations. The same extension supports physical units. See the mhchem manual for its equation and unit syntax.

Diagrams with Mermaid

Mermaid turns a text definition into a diagram in the browser. Use a mermaid code block:

```mermaid
flowchart LR
  Source --> Hugo --> Static
```
flowchart LR
  Source --> Hugo --> Static

The theme detects the block, publishes its pinned local Mermaid runtime, and loads it once on that page. Pages without Mermaid do not load the runtime.

Site-wide Mermaid settings live under params.mermaid:

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

Per-diagram front matter can override supported Mermaid settings. Keep diagram text readable in source, test both color modes, and provide surrounding prose for information that must remain accessible when a diagram cannot render.

UML diagrams with PlantUML

PlantUML supports sequence, use-case, class, state, and other UML-oriented diagrams. A plantuml block contains the source:

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

PlantUML requires a renderer endpoint. Enable it only with an approved local or explicit remote service:

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

The endpoint receives encoded diagram source from the browser. Review its confidentiality, availability, CSP, and offline implications. For an air-gapped site, use an internal endpoint or commit pre-rendered images; do not point the default configuration at a public demo server.

Mind-map support with Markmap

Markmap converts a Markdown outline into an interactive mind map:

```markmap
# Local-first
## Build
- Hugo Extended
## Browser
- Local scripts
- Local fonts
```
# Local-first
## Build
- Hugo Extended
## Browser
- Local scripts
- Local fonts

Enable the feature globally when desired:

params:
  markmap:
    enable: true

The runtime is pinned and served locally. Keep the underlying outline useful and avoid relying on pointer-only interactions.

Diagrams with Diagrams.net

Diagrams.net (draw.io) can export SVG and PNG files that retain an embedded copy of their editable diagram. OINK can detect those images and show an Edit action when an editor endpoint is explicitly configured.

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

Export with Include a copy of my diagram enabled. The page can display the exported image offline, but opening the editor requires the configured service. Saving in the editor downloads an updated file to the browser; it does not write directly to the documentation repository.

Treat a public Diagrams.net endpoint as an online integration. If editing must stay inside an organization, deploy an approved self-hosted editor and set drawio_server to it.

Resource and authoring checklist

  • Use text-based diagrams when reviewable diffs are valuable.
  • Provide alt text or adjacent prose for essential meaning.
  • Test light, dark, mobile, print, and reduced-motion behavior.
  • Keep local runtimes pinned in theme/VENDOR.json and load them only when used.
  • Never include secrets in diagram source sent to a service endpoint.
  • Use pre-rendered output when an online renderer is unacceptable.
  • Verify all asset and endpoint URLs under a subpath baseURL.

5 - Logos and Images

Add and customize logos, icons, and images in your project.

By default, Docsy shows a site logo at the start of the navbar, that is, at the extreme left. Place your project’s SVG logo in assets/icons/logo.svg. This overrides the default Docsy logo in the theme.

If you don’t want a logo to appear in the navbar, then set site parameter navbar_logo to false in your project’s config:

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

For information about styling your logo, see Styling your project logo and name.

Use icons

Docsy includes the free FontAwesome icons by default, including logos for sites like GitHub and Stack Overflow. You can view all available icons in the FontAwesome documentation, including the FontAwesome version when the icon was added and whether it is available for free tier users. Check Docsy’s package.json and release notes for Docsy’s currently included version of FontAwesome.

You can add FontAwesome icons to your navbar, side nav, or anywhere in your text.

Add your favicons

The theme ships no favicon files, but it discovers and links a set of conventionally named icons when you supply them: create your favicon files and put them in your site project’s static directory so they publish at the site root (where browsers probe for them). Docsy adds <link> elements inside each page’s <head> for whichever of these files it finds, in this order:

FileLink
favicon.icorel="icon"1
favicon.svgrel="icon" with type="image/svg+xml"
favicon-NxN.pngrel="icon" with type="image/png" sizes="NxN"
apple-touch-icon.pngrel="apple-touch-icon" (implicit size 180x180)
apple-touch-icon-NxN.pngrel="apple-touch-icon" with sizes="NxN"

If you have any square-size variants listed above, Docsy adds them in ascending size order.

A modern favicon.ico plus an SVG and an apple-touch-icon.png covers common browser and platform favicon needs. For anything beyond that:

  • Add web app manifest <link> elements to hooks/head-end.html.
  • If you need to customize the favicon links themselves, override layouts/_partials/favicons.html. Make sure you use relURL so links stay correct when your site’s baseURL includes a subpath.

Generate favicons

Don’t have a favicon yet? You can generate favicons from a single image with an online tool such as favicon.io or RealFaviconGenerator.

If you have a source SVG and ImageMagick installed, Docsy also ships a gen-favicons helper. Save your source SVG as static/favicon.svg – the theme links it directly – then generate the raster icons alongside it. Run the command from your site project root.

For an npm package install of Docsy:

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

Otherwise, run:

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

For a Git submodule install of Docsy, DOCSY_THEME_DIR is themes/docsy/theme. For a Hugo module install, it is the directory printed by go list -m -f '{{.Dir}}' github.com/google/docsy/theme.

For the sizes and other options you can pass, run the command with --help.

Add images

Landing pages

Docsy’s blocks/cover shortcode makes it easy to add cover images (also known as hero images) to landing pages. The shortcode looks for an image with the word “background” in the name within the landing page’s page bundle.

For example, the example site’s landing page content/en/_index.md uses the image content/en/featured-background.jpg, which is in the same directory – see the content/en folder on GitHub.

Use the block’s height parameter to set the preferred display height of the cover container (and therefore its image). For a full viewport height, use full, along with the td-below-navbar helper class to position the cover below the navbar:

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

For a shorter image, as in the example site’s About page, use one of min, med, max, or auto (the image’s natural height):

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

Other pages

To add inline images to other pages, use the imgproc shortcode. Alternatively, if you prefer, just use regular Markdown or HTML images and add your image files to your project’s static directory. You can find out more about using this directory in Adding static content.


  1. The .ico link carries no sizes: the file is self-describing (browsers read the frame sizes it contains), so declaring sizes here would only risk drifting from the actual file. When you also supply a favicon.svg, browsers that support SVG favicons (most modern ones) prefer it, and the .ico serves as the fallback. ↩︎

6 - Look and Feel

Customize OINK’s local-first visual system, themes, typography, code, and layouts.

OINK ships a complete visual system built on Bootstrap and Docsy, with local fonts, icons, styles, and browser code. A consuming site can change tokens and project styles without rebuilding a Node dependency tree.

Project styles

Hugo Extended compiles the theme’s SCSS through Hugo Pipes. Project overrides participate in the same bundle, so production builds can minify, fingerprint, and integrity-check one same-origin stylesheet.

Project style files

Override these files in the site’s assets/scss/ directory:

FilePurpose
_variables_project.scssVariables set before Bootstrap and OINK defaults
_variables_project_after_bs.scssVariables or maps that require Bootstrap definitions
_styles_project.scssProject selectors loaded after the theme’s component styles

Start with the smallest override:

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

Do not edit vendored Bootstrap, Font Awesome, or local font files for ordinary branding. A theme update would overwrite those changes and obscure the dependency boundary.

Advanced style customization

OINK’s SCSS import order is:

  1. Bootstrap functions;
  2. project variables;
  3. OINK defaults and Bootstrap;
  4. post-Bootstrap project variables;
  5. OINK components and local brand layer;
  6. project styles.

Use variables or CSS custom properties for stable design decisions. Override a selector only when no token exists, and scope it to the smallest component. Inspect both light and dark output because many colors are theme-dependent.

⚠️ Resetting internal styles

OINK’s internal partials are not a public Sass API. Importing or suppressing individual internal files couples a site to repository layout and import order. If a product needs a fundamentally different shell, override a Hugo layout or maintain a deliberate theme fork instead of resetting the entire stylesheet.

Extra styles

For isolated third-party CSS, publish a local asset from a hook:

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

Put the template in layouts/partials/hooks/head-end.html. Prefer the project SCSS files when the rules belong to the site’s design system. Never use a remote stylesheet as an implicit fallback.

Colors and color themes

Bootstrap semantic colors and OINK brand tokens are available throughout the theme. Semantic names communicate intent better than literal colors.

Site colors

Set Bootstrap variables before compilation:

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

OINK’s canonical layer also exposes CSS properties such as --td-brand-elev, --td-brand-silk, --td-brand-copper, --td-brand-header-bg, and --td-brand-mark-gradient. Override them on :root and [data-bs-theme='dark'] as a pair:

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

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

Light/dark color theme and mode support

Color theme is the palette used by a component; color mode is the site-wide light or dark state. OINK uses Bootstrap’s data-bs-theme="light|dark" attribute and stores an explicit reader choice in local browser storage. With no choice, it follows prefers-color-scheme.

Every custom component must define legible states for both modes, including hover, focus, disabled, selected, and code colors. Do not encode meaning by color alone.

Light/dark color modes

The default sample site enables color-mode support and shows the selector:

params:
  ui:
    showLightDarkModeMenu: true

The selector updates the document before normal interaction to limit a flash of the wrong theme. OINK’s script is local and does not contact an external service.

Choosing themes or color modes for your site

Use the default automatic behavior for most sites. Choose a forced mode only when the complete visual identity has been tested in that mode and readers do not need an alternative. Screenshots are not sufficient: check real text, tables, alerts, forms, diagrams, code, and focus indicators.

How to disable dark mode

To disable dark mode and hide the menu:

params:
  ui:
    showLightDarkModeMenu: false

The experimental value enable-only (experimental) enables theme-aware styles without showing a selector. Treat it as transitional because the configuration surface can change.

How to pick colors with good color-contrast

Meet WCAG contrast requirements in every component state. Test actual computed colors, including translucent layers over images. As a working minimum, normal text needs 4.5:1 contrast and large text needs 3:1; focus and non-text UI indicators also need adequate contrast. Automated tools catch common failures, but keyboard and visual review remain necessary.

Fonts

OINK does not fetch Google Fonts. Open Sans, Chakra Petch, IBM Plex Mono, and Font Awesome files used by the theme are stored locally. The legacy Sass variable $td-enable-google-fonts controls the bundled Open Sans faces despite its historical name.

Set typography in _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;

If you add a font, subset and self-host it, include the required scripts, use font-display: swap, document its license in theme/VENDOR.json, and test CJK fallback. Do not make page rendering depend on a font CDN.

CSS utilities

Bootstrap utility classes are available in Markdown with raw HTML and in layouts. Prefer semantic Markdown and OINK shortcodes for content; use utilities for small, presentational adjustments that remain understandable at different breakpoints. Project-wide patterns belong in _styles_project.scss.

Code blocks

OINK supports Hugo Chroma by default and a locally vendored Prism option. Choose one highlighter consistently; enabling both produces duplicate markup or styles.

Code highlighting with Chroma

Chroma runs during the Hugo build and requires no browser highlighter. Use a language identifier:

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

Basic Chroma style configuration

Configure markup in Hugo:

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

OINK expects class-based output so light and dark styles can differ. When regenerating a palette, keep the generated CSS local and review it against the brand background.

Light/dark code styles and more

The theme includes separate Chroma palettes under theme/assets/scss/td/chroma/ and applies them by mode. Project overrides should target .chroma beneath the relevant theme attribute, not hard-code a global background.

Selecting console block content

Use console for terminal transcripts. OINK styles prompts and output for selection so readers can copy commands without decorative prompt text. Keep commands and their output on distinct lines, and never rely on color alone to distinguish them.

Code blocks without a specified language

An unlabelled fence renders as plain code. Use it only when no grammar applies, and label command sessions as console or bash instead of asking Chroma to guess.

Copy to clipboard

Copy buttons are enabled for Chroma unless params.disable_click2copy_chroma is true. Clipboard access requires a secure context in deployed browsers. The control must remain keyboard accessible and must not copy line numbers or prompts.

Code highlighting with Prism

Set:

params:
  prism_syntax_highlighting: true

to use OINK’s local prism.js and prism.css. This is a compatibility option for existing sites; Chroma is preferred for a browser-light build.

Code blocks with no language

Prism also treats unlabelled blocks as plain text. Add the correct language class rather than enabling heuristic detection.

Extending Prism for additional languages or plugins

Build and vendor the exact Prism bundle, replace the local files in a controlled theme change, record its version and license, and add a fixture that exercises the language or plugin. Do not pull Prism components from a CDN at runtime.

OINK’s navbar contains the project identity, main menu, version and language selectors when applicable, color-mode control, and search. On small screens, overflowing primary items remain horizontally reachable.

Default look and feel

The navbar uses the local brand palette and a fixed minimum height.

On mobile

The brand and actions stay visible while the primary menu can scroll. Test long Chinese labels, 200% zoom, touch targets, focus order, and both page directions.

On desktop

The main menu expands inline; version, language, mode, and search controls stay grouped. Avoid enough custom entries to push controls outside the viewport.

Translucent over cover images

The blocks/cover shortcode marks the navbar as cover-aware. It starts translucent and gains the normal background as the page scrolls.

Use configuration for behavior and project SCSS for presentation. Preserve the landmark, focus order, accessible labels, and responsive overflow behavior when overriding the navbar partial.

Override $td-navbar-min-height before theme styles compile. Re-test anchor offsets, sidebar height, mobile wrapping, and cover blocks because all depend on this value.

Set --td-navbar-bg-color or --td-brand-header-bg in both modes. If the background is translucent, validate contrast over every cover image and provide a solid scrolled state.

A page can set ui.navbar_theme: dark in front matter or cascade when its cover requires light foreground controls. This changes navbar component styling; it does not force the whole site’s color mode.

Translucent over cover images

Disable translucency site-wide with:

params:
  ui:
    navbar_translucent_over_cover_disable: true

Prefer this when cover imagery is unpredictable or accessibility review cannot guarantee contrast.

Styling your project logo and name

Place logo partial overrides under layouts/partials/ and source assets under assets/ or static/. Provide meaningful alternative text for informative marks and an empty alternative for a purely decorative mark. SVGs must use a view box and inherit or define colors for both modes.

The OINK sample uses a text wordmark with a local gradient. Change the site title in language configuration and the visual tokens in project SCSS; do not replace brand text with an image when selectable text works.

Light/dark-mode menu

The selector appears when params.ui.showLightDarkModeMenu is true. Keep it in the shared navigation so its state applies consistently across languages and page types.

Alerts

Markdown alert types map to semantic OINK/Bootstrap styles. Customize .alert-* and the alert render hook only as a pair, retain a visible label or icon, and test links and inline code inside every background. See Adding Content for syntax.

Tables

Markdown tables receive responsive and theme-aware styles. Keep cells concise, use real header cells, add a caption in custom HTML when context requires one, and test horizontal overflow on mobile. A table should not be used to position unrelated content.

Customizing templates

Hugo resolves site layouts before theme layouts. Copy only the smallest partial that needs changing and compare it during upstream syncs; a full baseof.html override can silently miss future accessibility and asset-pipeline fixes.

Add code to head or before body end

Use layouts/partials/hooks/head-end.html for head additions and layouts/partials/hooks/body-end.html for scripts or closing integrations. Self-host assets, load them only on pages that need them, and keep production CSP compatible.

Adding a banner before page content

Override the relevant hook or content partial with a condition based on page parameters. A banner must not hide the page heading, trap keyboard focus, or shift anchor targets beneath the fixed navigation.

Adding custom class to the body element

Set body_class in page front matter or a section cascade:

---
body_class: product-reference
---

OINK appends the value to its generated body classes. Use a project-specific, semantic class name and never insert untrusted content into this field.

7 - Navigation and Menus

Configure OINK navigation, language switching, sidebars, and page outlines.

OINK combines Hugo’s content tree and menu model with a documentation workspace: a global navbar, a collapsible and resizable section sidebar, and a collapsible page outline. The same structure works for English, Chinese, and right-to-left languages.

The global navbar is built from Hugo’s main menu plus OINK-generated controls. Depending on configuration and page type, it can include version, language, color-mode, and search controls.

Adding main menu entries

Define a menu entry in page front matter:

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

Lower weights appear first. A site-level external link is similar:

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>

Use an identifier for configuration that refers to a menu item. Localize name or linkTitle in language configuration, but keep identifiers stable.

Version menu

The selector appears when params.versions is configured. Each entry can be a heading, separator, release, development build, or site variant:

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 identifies the published site variant and is not necessarily a Git ref. Commands that require a resolvable tag should use the project’s explicit release-ref parameter instead. With page links enabled, OINK first tries the equivalent path on the target version and otherwise uses its configured URL.

Language menu

OINK builds language targets from Hugo’s AllTranslations. When a translated peer is missing, the target language’s home page is used instead of a broken URL. One configured language hides the control. With two or more languages, a click advances to the next language by weight, while hovering for half a second or focusing the control opens the complete menu. The current site cycles from English to Simplified Chinese and back. Targets include lang, hreflang, locale, and text-direction attributes.

Light/dark theme menu

When color-mode support is enabled, the navbar and documentation workspace show a theme control. See Light/dark-mode menu.

The documentation workspace uses a local search dialog when offline search is enabled. The sidebar button advertises the platform shortcut (Command/Ctrl+K). Online search integrations remain available by explicit configuration. See Search.

Adding icons to the navbar

Use pre or post on a menu entry. OINK includes the free local Font Awesome assets:

menus:
  main:
    - name: Source
      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"> (external)</span>

Decorative icons need aria-hidden="true"; the link itself must retain a useful text or accessible label. External links that open a new tab must use rel="noopener".

Side navigation

The left panel on docs and blog pages is generated from the content hierarchy. OINK orders entries by weight and uses linkTitle when present. Sections come from _index.md files; translated sections need a peer _index.zh.md so their navigation metadata is localized.

Hide a page from the sidebar with:

toc_hide: true

Hide it from a section landing-page summary with hide_summary: true. Set both only when the page should be absent from both discovery surfaces.

Side-nav options

The common controls are:

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 shows the active branch and nearby entries.
  • sidebar_menu_foldable lets readers expand or collapse sections.
  • sidebar_menu_truncate limits entries and emits a build warning when the limit is too small.
  • sidebar_cache_limit enables shared navigation markup above the configured site size.
  • sidebar_width_min and sidebar_width_max clamp the desktop drag-resizer.
  • sidebar_item_overflow is ellipsis by default; use wrap for long labels.

The reader’s collapse state, width, and scroll position are preserved locally. The mobile view becomes a dismissible drawer with a backdrop and focus-safe controls.

Adding icons to the side nav

Set icon in page front matter:

---
title: Operations
icon: fa-solid fa-screwdriver-wrench
---

Use icons consistently across siblings. They are secondary cues, not a replacement for text labels.

Create a placeholder page at the desired position:

---
title: API status
weight: 90
manualLink: https://status.example.org/
manualLinkTitle: Live service status
manualLinkTarget: _blank
---

Use manualLinkRelref instead of manualLink for an internal content reference; Hugo then fails the build if it cannot resolve the destination. OINK adds noopener for new-tab links. Include a short body explaining the destination because Hugo still generates a page for the placeholder.

Enable rooted sidebars:

params:
  ui:
    sidebar_root_enabled: true
    sidebar_root_menu: true

Then set a section’s _index.md:

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

self applies the root to the section index and descendants; children keeps the index in the parent tree but roots its descendants. The optional root menu lets readers switch between roots. Rooted sections can nest, but redundant or invalid values produce build warnings.

Table of contents (TOC)

Hugo builds the right-side page outline from Markdown headings. OINK renders it as a fixed documentation panel with quick links, language and theme controls, repository metadata, and taxonomy terms. Readers can collapse the panel; its state is stored locally.

Headings emitted by Markdown shortcodes ({{% ... %}}) participate in Hugo’s table of contents. Headings emitted only by standard shortcodes ({{< ... >}}) generally do not, so content structure should remain in Markdown whenever possible.

TOC customization

Hide the outline on one page:

notoc: true

Configure which heading levels Hugo includes:

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

Localize labels such as toc_on_this_page in the site’s i18n bundle. If custom CSS changes the outline rail or fixed-panel dimensions, test active tracking, zoom, keyboard focus, and pages with no headings.

Active TOC entry tracking with ScrollSpy

OINK uses a local Bootstrap ScrollSpy patch and IntersectionObserver to track the active heading. The workspace draws a continuous rail, active segment, and position marker. Disable tracking for a page with:

params:
  ui:
    scrollSpy:
      disable: true

The legacy ScrollSpy configuration also accepts a global rootMargin. Changing it affects when an entry becomes active and should be tested with short sections, long sections, and direct fragment navigation.

Advanced ScrollSpy customization

Prefer configuration and project CSS. Overriding the ScrollSpy attribute partial or docs-shell.js creates an implementation-level fork; add browser fixtures for hash updates, back/forward navigation, resizing, reduced motion, and pages that contain duplicate or missing IDs.

Breadcrumbs are shown above ordinary content pages and in taxonomy results. Disable them globally:

params:
  ui:
    breadcrumb_disable: true
    taxonomy_breadcrumb_disable: true

The same ui.breadcrumb_disable value can be set in a page or section cascade. Breadcrumb labels come from localized page titles and must follow the same logical hierarchy as the sidebar.

Enable OINK’s heading render hook in a consuming site:

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

The generated .td-heading-self-link control uses # by default. It remains visible on touch devices and appears on hover or focus for pointer devices. Keep the link keyboard reachable and preserve a scroll offset that clears fixed navigation.

Heading aliases and in-page targets

Changing a heading can break inbound fragment links. Treat its ID as a public route. To rename an ID, retain the old one as an empty anchor and set the new one explicitly:

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

Use an empty <a id="..."></a> for an alias or other in-page target. Do not use a span solely as a fragment target. IDs must be unique, stable, ASCII where practical, and identical across language variants.

Quickstart

This live heading demonstrates that both #get-started and #quickstart reach the same location. Translated headings should write the English rendered ID explicitly rather than relying on language-specific automatic slug generation.

Implementation notes

  • The document sets a global scroll offset for fixed chrome.
  • Built-in block targets use td-anchor-no-extra-offset to avoid applying the additional offset twice.
  • The translation audit compares rendered heading IDs between English and Chinese pages.
  • Removing an old alias is a breaking documentation change and needs a redirect or an explicitly documented compatibility decision.

8 - Print Support

Making it easier to print entire sections of documentation.

Individual documentation pages print well from most browsers as the layouts have been styled to omit navigational chrome from the printed output.

On some sites, it can be useful to enable a “print entire section” feature (as seen in this user guide). Selecting this option renders the entire current top-level section (such as Content and Customization for this page) with all of its child pages and sections in a format suited to printing, complete with a table of contents for the section.

To enable this feature, add the “print” output format in your site’s hugo.toml/hugo.yaml/hugo.json file for the “section” type:

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

The site should then show a “Print entire section” link in the right hand navigation.

Further Customization

Disabling the ToC

To disable showing the table of contents in the printable view, set the disable_toc param to true, either in the page front matter, or in hugo.toml/hugo.yaml/hugo.json:

+++

disable_toc = true

+++
---

disable_toc: true

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

Layout hooks

A number of layout partials and hooks are defined that can be used to customize the printed format. These can be found in layouts/_partials/print.

Hooks can be defined on a per-type basis. For example, you may want to customize the layouts of heading for “blog” pages vs “docs”. This can be achieved by creating layouts/_partials/print/page-heading-<type>.html such as page-heading-blog.html. It defaults to using the page title and description as a heading.

Similarly, the formatting for each page can be customized by creating layouts/_partials/print/content-<type>.html.

9 - Repository links and page information

Help readers inspect, edit, and report issues against page source.

OINK’s documentation and blog layouts can show links to the current page’s source repository:

  • View page source opens the source file.
  • Edit this page opens an editable source view.
  • Create child page starts a new file below the current page and can use the site’s assets/stubs/new-page-template.md template.
  • Create documentation issue opens an issue against the documentation repository with page context.
  • Create project issue optionally targets a separate product repository.

The built-in URL patterns target GitHub-style repositories. Verify every action when using another compatible host, and override the relevant partial for a different URL scheme.

A typical site configuration is:

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

The values can be set globally, per language, in a section cascade, or in page front matter when content comes from more than one repository.

github_repo

The documentation source repository URL. It drives view, edit, child-page, and documentation-issue links:

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

Omit it to suppress repository-derived page actions. Do not point it at the theme repository when the page source actually lives in a consuming site.

github_subdir (optional)

Set the path from the repository root to the Hugo site source. This project stores its site in oink.pgsty.com:

params:
  github_subdir: oink.pgsty.com

The value is a repository path, not a local absolute path and not the content directory itself unless that is the actual site root.

github_project_repo (optional)

Set a separate product repository to show Create project issue:

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

Use the documentation repository for content defects and the product repository for behavior discussed by the page. If that distinction is not clear to readers, omit the second link.

github_branch (optional)

Set the branch used by source and edit URLs:

params:
  github_branch: main

This is normally the site’s source branch. It is not necessarily the deployed branch, generated Pages branch, or theme revision.

path_base_for_github_subdir (optional)

Use a section cascade when a subtree is mounted from another repository. The path base is removed before the remaining content path is appended to 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
---

For a source page at content/reference/api/client.md, this configuration maps the repository path to docs/api/client.md.

path_base_for_github_subdir can be a regular expression. A language-directory site might use:

path_base_for_github_subdir: content/\w+/reference

OINK’s colocated .md / .zh.md layout normally uses the same static base for both languages and does not need the language component in this expression.

When the source file has another name, use a from and to mapping. This example maps a section _index.md to an upstream README.md:

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

Test view and edit links from a leaf page, a section page, and both language versions. A regular expression that removes too much can produce a plausible but incorrect repository URL.

github_url (optional)

A legacy page can set a complete custom edit URL in front matter:

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

Pages using this value expose only Edit this page. A site-specific template override is preferable when the destination is not GitHub-compatible.

Each action has a stable CSS class:

LinkClass
View page source.td-page-meta__view
Edit this page.td-page-meta__edit
Create child page.td-page-meta__child
Create documentation issue.td-page-meta__issue
Create project issue.td-page-meta__project-issue

Hide an action in assets/scss/_styles_project.scss when the destination does not support it:

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

Prefer omitting an unavailable global destination in configuration. CSS hiding is useful for selective policy; it does not make a malformed link correct.

Last-modified page metadata

Enable Hugo Git information and configure the source repository:

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

OINK can then show the last commit date, subject, hash, and source link on documentation and blog pages. CI must fetch enough Git history for the current file; shallow checkouts can produce missing or misleading metadata.

To hide the note for a particular site or section, override its style or the responsible page-meta partial. Do not label a file “last modified” from the build timestamp when Git history is unavailable.

10 - Search

Configure local multilingual search or an explicit online provider.

OINK’s default and recommended search is local. Hugo generates a per-language index; the theme serves Lunr and its CJK fallback from same-origin assets. The site can build and search without a public crawler, external account, CDN, or network connection.

Google Custom Search and Algolia DocSearch remain compatible online integrations. They are disabled by default and should be enabled only when the site accepts their external requests, indexing, availability, and privacy boundaries.

Only one search implementation can be active at a time.

Local search with Lunr

Enable local search in hugo.yaml:

params:
  offlineSearch: true

Do not configure gcs_engine_id or params.search.algolia at the same time. After a production build, the output contains one index per language, for example:

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

The browser loads the active language’s index and displays results without leaving the page. Chinese content uses OINK’s CJK fallback instead of depending on whitespace tokenization.

Build the index before testing

Run a normal build before starting a preview:

hugo --gc
hugo server --disableFastRender

If the server was already running when the index changed, restart it. On a subpath deployment, confirm that the browser requests the index under the configured baseURL rather than from the domain root.

Configure result summaries and limits

Set the summary length and maximum result count:

params:
  offlineSearch: true
  offlineSearchSummaryLength: 120
  offlineSearchMaxResults: 12

Choose limits that keep the search dialog responsive on mobile devices. The summary is a discovery aid, not a replacement for a well-written page description.

Exclude a page

Set exclude_search: true in page front matter:

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

Use this for utility, duplicate, generated, or test pages. Do not exclude a page only because its current translation is incomplete; fix the translation instead.

Style the result panel

The result panel grows with its content. A site can constrain it in assets/scss/_styles_project.scss:

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

Preserve keyboard focus, visible selection, mobile width, and dark-mode contrast when overriding search styles.

Search entry points

OINK exposes search from the branded shell and can also show a sidebar input. To hide the sidebar input while retaining the main search entry, configure:

params:
  ui:
    sidebar_search_disable: true

The shell’s open and close controls expose their dialog relationship and state to assistive technology. A custom implementation must preserve those semantics.

Search stays in the active language. Verify that:

  • every published language has its own index;
  • translated titles, descriptions, and body text appear in that index;
  • a result URL contains the correct language prefix;
  • English results do not replace Chinese results through content fallback;
  • the language selector on a result page reaches the corresponding translation or the documented language-home fallback.

For Chinese search failures, inspect the generated Chinese JSON before changing tokenization. A missing or English-only index is usually a content or build configuration problem.

Google Custom Search Engine (GCSE) searches a public site through Google’s index. It requires a deployed, crawlable production site and sends queries to a third-party service.

After creating an engine in Google Programmable Search, add a search result page:

---
title: Search results
layout: search
---

Then configure its engine ID:

params:
  gcs_engine_id: YOUR_ENGINE_ID
  offlineSearch: false

Create a translated result page for every supported language and use a language-appropriate engine configuration when needed. Removing gcs_engine_id disables GCSE.

Document the external request and privacy implications in the consuming site’s policy. GCSE is not available in an air-gapped deployment.

Algolia DocSearch (optional)

Algolia DocSearch provides a hosted crawler and interactive result panel for eligible public documentation sites. Obtain the project’s application ID, search API key, and index name, then configure:

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

Use a search-only public key, never an administrative key. Keep crawler rules, language facets, index updates, and external-service disclosure with the site configuration. This integration is intentionally separate from the local-first default.

The theme partials layouts/_partials/algolia/head.html and layouts/_partials/algolia/scripts.html can be overridden for a site-specific integration. An empty override disables that theme partial.

If none of the supported choices fits, a site can replace the search input, result behavior, and styles. Reuse the shell’s dialog and accessibility contracts where possible. Keep custom code at the site layer unless it is provider-neutral and reusable across multiple products.

A custom online provider must be opt-in and document its network, privacy, indexing, failure, and offline behavior. A custom local provider must publish all runtime assets from the site or theme and respect language and baseURL boundaries.

11 - Shortcodes

Use OINK’s local-first content components safely and accessibly.

Shortcodes add behavior that ordinary Markdown cannot express. OINK retains the core Docsy components and adds locally served charts, terminal recordings, infographics, carousels, cards, and disclosure widgets. Browser runtimes load only on pages that use them.

Prefer Markdown for headings, prose, lists, links, tables, and images. A shortcode becomes part of the content API: changing its name or parameters can break every page that calls it.

Shortcode delimiters

Hugo supports two forms:

  • {{< name >}} uses standard delimiters and passes inner content as-is;
  • {{% name %}} uses Markdown delimiters and renders inner Markdown in the surrounding content context.

Use the form documented for the component. Nesting, indentation, and blank lines matter, especially inside lists and blockquotes. In examples, the /* ... */ escape prevents Hugo from executing the displayed shortcode.

blocks/* shortcodes

Block shortcodes compose full-width landing pages. Their color argument uses OINK/Bootstrap semantic colors or a project-defined block style. Their height argument accepts the values documented for each block.

blocks/cover

Creates a hero from the page bundle image matching *background* and optional *logo*:

{{< blocks/cover title="OINK" subtitle="Local-first documentation"
    color="dark" height="max" >}} [Get started](/docs/get-started/){ .btn
.btn-lg .btn-primary } {{< /blocks/cover >}}

image_anchor and logo_anchor control image cropping; byline attributes the image. Heights are auto, min, med, max, or full. Essential hero text must remain readable without the background.

blocks/lead

Creates a prominent introductory band:

{{% blocks/lead color="primary" height="min" %}} OINK builds the whole
documentation experience with Hugo Extended. {{% /blocks/lead %}}

The height accepts auto, min, med, max, or full.

blocks/section

Creates a general landing-page band:

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

### One section

Use ordinary Markdown inside the block. {{% /blocks/section %}}

type selects the container treatment; height uses the block height values. Keep heading levels consistent with the page outline.

blocks/feature

Creates one feature cell, normally inside a section:

{{% blocks/feature icon="fa-solid fa-box-archive"
    title="Works offline" url="/docs/oink/local-first/"
    url_text="Read the design" %}} All required browser assets are pinned and
served locally. {{% /blocks/feature %}}

The icon is decorative; title and link text must carry the meaning.

Adds a link from one block to the next. It must be nested inside a block. Set an explicit id when the generated target must remain stable.

Below-navbar layout correction

Blocks that begin directly below fixed navigation use td-below-navbar/td-anchor-no-extra-offset to compensate for navbar height. Reuse these classes rather than adding arbitrary top margins; verify direct fragment navigation after changing navbar dimensions.

Helper shortcodes

alert

The legacy alert shortcode remains available:

{{% alert title="Compatibility note" color="warning" %}} Prefer Markdown
blockquote alerts for new content. {{% /alert %}}

color maps to a Bootstrap alert suffix. New content should generally use the Markdown alert syntax described in Adding Content.

Alerts, indentation, and examples

Keep the opening and closing shortcode aligned with their surrounding list or blockquote. Leave a blank line around block Markdown. If an example must show a shortcode literally, escape its delimiters rather than wrapping an active call in another component.

pageinfo

Renders an informational panel around Markdown:

{{% pageinfo color="info" %}} This page describes a preview interface.
{{% /pageinfo %}}

Use a semantic alert for warnings; pageinfo is intended for contextual page information.

imgproc

Processes an image from the current page bundle:

{{% imgproc "architecture" Fit "960x540" %}} OINK runtime architecture.
{{% /imgproc %}}

Commands are Fit, Resize, Fill, and Crop. The third argument follows Hugo image-processing syntax. The inner text becomes a caption, and a resource params.byline is appended when present. Always provide useful alternative or adjacent text.

swaggerui

Embeds the locally vendored Swagger UI runtime:

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

Use a same-origin specification for offline and CSP-safe deployments. A remote src is an explicit network dependency and can expose reader metadata to that host. Only one Swagger UI instance should be placed on a page with the current compatibility shortcode.

redoc

Embeds the locally vendored Redoc runtime:

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

The first argument is a page-relative, site-relative, or explicit HTTP specification. The optional second argument contains Redoc element options. Treat specification content as reviewed input and test large schemas on mobile.

iframe

Embeds another page:

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

Set a descriptive name, a unique id, a fallback sub message, and the narrowest viable sandbox. The defaults support width and automatic-height behavior, but cross-origin documents cannot always be measured. An iframe is a security and privacy boundary, not a general layout tool.

OINK content components

The following components are additions carried by OINK. Each runtime is pinned in theme/VENDOR.json and loaded on demand from the same origin.

details

Creates an accessible disclosure:

{{% details title="Show migration notes" closed="false" %}} The body accepts
Markdown. {{% /details %}}

closed defaults to true. Use a concise summary and do not hide mandatory instructions inside a closed disclosure.

asciinema

Plays an asciinema .cast recording:

{{< asciinema file="casts/install.cast" speed="1.25"
    markers="0:Start,18:Verify" fit="width" >}}

Important parameters include theme, autoplay, loop, preload, speed, startAt, poster, cols, rows, idleTimeLimit, pauseOnMarkers, markers, and fit (width, height, both, or none). Local recordings can come from Hugo assets or a site-relative URL. Avoid autoplay, remove secrets from terminal history, and provide nearby text for essential steps.

echarts

Renders an Apache ECharts options object from JSON or YAML:

{{< echarts height="320px" >}} xAxis: type: category data: [Build, Test,
Publish] yAxis: type: value series:

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

height must be a safe CSS length; theme selects an ECharts theme and full=true removes the normal content-width clamp.

JavaScript blocks inside the shortcode are rejected by default. They require unsafe=true on that call or params.content.echarts_unsafe=true. This opt-in allows executable content and must never be enabled for untrusted authors. Prefer declarative JSON/YAML, add an adjacent textual summary, and verify dark mode.

infographic

Renders the locally vendored infographic DSL:

{{< infographic height="360px" >}} infographic
list-row-simple-horizontal-arrow data items - label Build - label Test - label
Publish {{< /infographic >}}

height is auto or a safe CSS length; full=true removes the width clamp. The DSL is data, not arbitrary HTML. Provide prose that communicates the same conclusion when the visualization is unavailable.

doc-cards and nav-cards

Both containers accept cols from 1 through 4. Their child cards accept title, link, image, alt, icon, desc, accent, and badge:

{{< nav-cards cols="2" >}}
{{< nav-card title="Get started" link="/docs/get-started/"
      icon="fa-solid fa-rocket" desc="Build with Hugo {version}." >}} {{< nav-card title="Architecture" link="/docs/oink/architecture/"
      badge="Design" >}}
{{< /nav-cards >}}

doc-card/doc-cards share the rendering contract and suit editorial content; nav-card/nav-cards signal navigation. Description tokens such as {version} resolve from site parameters. Card images are lazy-loaded; supply meaningful alt text unless the image is decorative.

Places doc-card elements in a keyboard-scrollable carousel:

{{< doc-carousel label="Release highlights" >}}
{{< doc-card title="Local assets" >}}No CDN required.{{< /doc-card >}}
{{< doc-card title="Bilingual" >}}Stable English and Chinese
routes.{{< /doc-card >}} {{< /doc-carousel >}}

label names the region for assistive technology. Previous/next buttons are localized. Do not place information only in an off-screen card; the track must remain usable without script.

param

Prints a page parameter, falling back through Hugo’s Page.Param rules to site configuration:

OINK version {{< param version >}}.

A missing parameter fails the build. Use param for scalar display values, not for injecting unreviewed HTML. The internal _param compatibility shortcode also performs numbered placeholder replacement for legacy content.

Tabbed panes

Tabs group equivalent representations, such as YAML/TOML/JSON configuration. They must not hide sequential steps or unrelated choices.

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

Selection persistence is local to the browser. persist accepts header, lang, or disabled. The deprecated persistLang should not be used in new content.

Shortcode details

text=true renders inner content as prose rather than highlighted code. right=true aligns tabs to the end. langEqualsHeader=true derives language identifiers from headers. Pane defaults can be overridden per tab.

tabpane

The parent validates boolean and persistence parameters, builds unique IDs, and ensures a selected tab. Use one disabled header tab only when it adds a useful group label.

tab

tab must be inside tabpane. It accepts header, selected, lang, highlight, text, right, and disabled. Only one tab should be selected. Translate reader-facing headers, but keep language identifiers stable.

Card panes

The legacy cardpane/card pair lays out Bootstrap-style cards. New navigation surfaces should prefer OINK content cards, but existing Docsy content can keep the compatibility component.

Shortcode card: textual content

{{% cardpane %}}
{{% card header="Note" title="Local build" footer="Verified" %}} Markdown
**content**. {{% /card %}} {{% /cardpane %}}

header, title, subtitle, and footer accept rendered text. Keep equal cards concise and avoid using cards as a replacement for headings.

Shortcode card: programming code

Set code=true and optionally lang/highlight:

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

Card groups

Adjacent cards in cardpane form a responsive group. Test unequal text length, mobile stacking, code overflow, and both language variants.

Include external files

The readfile shortcode reads a repository file at build time and either renders it as Markdown or highlights it as code. The path is relative to the current content file unless it begins with /.

Reuse documentation

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

Included Markdown is not an independent published page and is exempt from the page-pair audit. If shared prose is reader-facing, create and select language-specific include files deliberately; Hugo cannot translate an include.

Installation

Keep reusable fragments under an includes/ directory near their callers. Document ownership and avoid deep include chains: readers and reviewers should be able to locate the source quickly.

Include code files

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

code=true highlights the file with lang. Never include secrets, generated credentials, or untrusted paths.

Error reporting

A missing file fails the build. draft=true replaces that failure with a visible draft warning, which is suitable only during authoring and must not reach a release build.

Conditional text

conditional-text selects content using params.buildCondition:

{{% conditional-text include-if="enterprise,preview" %}} This paragraph
appears only in matching builds. {{% /conditional-text %}}

include-if and exclude-if accept condition lists. A condition cannot appear in both. Use the feature for genuinely different published variants, not for language selection; multilingual content belongs in translated page files.

12 - Taxonomy Support

Structure the content using taxonomies like tags, categories, labels.

Docsy supports Hugo taxonomies in its docs and blog section. You can see the default layout and can test the behavior of the generated links on this page.

Terminology

To understand the usage of taxonomies you should understand the following terminology:

  • Taxonomy: a categorization that can be used to classify content - e.g.: Tags, Categories, Projects, People

  • Term: a key within the taxonomy - e.g. within projects: Project A, Project B

  • Value: a piece of content assigned to a term - e.g. a page of your site, that belongs to a specific project

A movie-website sample taxonomy is provided by the Hugo docs.

Parameters

There are various parameters to control the functionality of taxonomies in the project configuration file. Taxonomies are enabled by default for tags and categories in Hugo. To disable taxonomies, add the following to your project config:

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

Then the taxonomy pages for tags and categories will be generated by Hugo. If you want to use other taxonomies you have to define them in your configuration file. If you want to use beside your own taxonomies also the default taxonomies tags and categories, you also have to define them beside your own taxonomies. You need to provide both the plural and singular labels for each taxonomy.

With the following example you define a additional taxonomy projects beside the default taxonomies tags and categories:

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

You can use the following parameters in your project’s config to control the output of the assigned taxonomy terms for each article resp. page of your docs and/or blog section in Docsy or a “tag cloud” in Docsy’s right sidebar:

[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"
      ]
    }
  }
}

The settings above would only show a taxonomy cloud for projects and tags (with the headlines “Our Projects” and “Tag Cloud”) in Docsy’s right sidebar and the assigned terms for the taxonomies tags and categories for each page.

To disable any taxonomy cloud you have to set the Parameter taxonomyCloud = [] resp. if you don’t want to show the assigned terms you have to set taxonomyPageHeader = [].

By default, the plural label of a taxonomy is used as its cloud title. You can override the default cloud title with taxonomyCloudTitle. But if you do so, you have to define a manual title for each enabled taxonomy cloud (taxonomyCloud and taxonomyCloudTitle must have the same length!).

If you don’t set the parameters taxonomyCloud resp. taxonomyPageHeader the taxonomy clouds resp. assigned terms for all defined taxonomies will be generated.

Partials

The partials used by default for displaying taxonomies are defined so that you can easily use them in your own layouts.

taxonomy_terms_article

The partial taxonomy_terms_article shows all assigned terms of a given taxonomy (partial parameter taxo) of an article respectively page (partial parameter context, most of the time the current page or context .).

Example usage in layouts/docs/list.html for the header of each page in the docs section:

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

This will give you for each in the current page (resp. context) defined taxonomy a list with all assigned terms:

<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

The partial taxonomy_terms_article_wrapper is a wrapper for the partial taxonomy_terms_article with the only parameter context (most of the time the current page or context .) and checks the taxonomy parameters of your project’s hugo.toml/hugo.yaml/hugo.json to loop through all listed taxonomies in the parameter taxonomyPageHeader resp. all defined taxonomies of your page, if taxonomyPageHeader isn’t set.

taxonomy_terms_cloud

The partial taxonomy_terms_cloud shows all used terms of a given taxonomy (partial parameter taxo) for your site (partial parameter context, most of the time the current page or context .) and with the parameter title as headline.

Example usage in partial taxonomy_terms_clouds for showing all defined taxonomies and its terms:

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

This will give you the following HTML markup for the taxonomy categories:

<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

The partial taxonomy_terms_clouds is a wrapper for the partial taxonomy_terms_cloud with the only parameter context (most of the time the current page or context .) and checks the taxonomy parameters of your project’s config to loop through all listed taxonomies in the parameter taxonomyCloud resp. all defined taxonomies of your page, if taxonomyCloud isn’t set.

Multi language support for taxonomies

For multilingual sites, taxonomy terms get counted and linked within the language site only. Taxonomy config parameters can be adjusted per language.

13 - Versioning

Customize navigation and banners for multiple versions of your docs.

Depending on your project’s releases and versioning, you may want to let your users access previous versions of your documentation. How you deploy the previous versions is up to you. This page describes the Docsy features that you can use to provide navigation between the various versions of your docs and to display an information banner on the archived sites.

Adding a version drop-down menu

If you add some [params.versions] in hugo.toml/hugo.yaml/hugo.json, the Docsy theme adds a version selector drop down to the navbar. You specify a URL and a name for each version you would like to add to the menu, as in the following example:

# 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"
      }
    ]
  }
}

Remember to add your current version so that users can navigate back!

The default title for the version drop-down menu is Releases. To change the title, change the site parameter version_menu in hugo.toml/hugo.yaml/hugo.json:

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

If you set the version_menu_pagelinks parameter to true, then links in the version drop-down menu point to the current page in the other version, instead of the main page. This can be useful if the document doesn’t change much between the different versions. Note that if the current page doesn’t exist in the other version, the link will be broken.

You can also configure individual menu entries:

  • Use name instead of version when the menu label is not a version number.
  • Set name to --- to add a menu separator.
  • Omit url to render a disabled text item, such as a group heading.
  • Set kind to add a kind-specific class for styling. For details, see Navigation and menus.
  • Set pagelinks: false on an entry to link to that version’s main URL even when the global version_menu_pagelinks parameter is true.

For example:

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

To learn more about Docsy menus, see Navigation and menus.

Displaying a banner on archived doc sites

If you create archived snapshots for older versions of your docs, you can add a note at the top of every page in the archived docs to let readers know that they’re seeing an unmaintained snapshot and give them a link to the latest version.

For example, see the archived docs for Kubeflow v0.6:

A text box explaining that this is an unmaintained snapshot of the docs.
Figure 1. The banner on the archived docs for Kubeflow v0.6

To add the banner to your doc site, make the following changes in your hugo.toml/hugo.yaml/hugo.json file:

  1. Set the site parameter archived_version to true:

    [params]
    archived_version = true
    params:
      archived_version: true
    {
      "params": {
        "archived_version": true
      }
    }
  2. Set the site parameter version to the version of the archived doc set. For example, if the archived docs are for version 0.1:

    [params]
    version = "0.1"
    params:
      version: 0.1
    {
      "params": {
        "version": "0.1"
      }
    }
  3. Make sure that site parameter url_latest_version contains the URL of the website that you want to point readers to. In most cases, this should be the URL of the latest version of your docs:

    [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"
      }
    }