This is the multi-page printable view of this section. .
Site configuration
- 1: Configuration
- 2: Navigation and menus
- 3: Languages
- 4: Versions
- 5: Repository links and page information
Once the site runs, this chapter covers site-level structural configuration: how readers navigate, how many languages the content has, how many versions the documentation carries, and which repository the page actions point at.
How to write individual pages is in Authoring; the available building blocks are in Components.
In this chapter
- Configuration overview: the complete
hugo.yamlsurface - Navigation and menus: top menu, sidebar, breadcrumbs
- Languages: language routing, translation fallback, RTL
- Versions: multi-version documentation and switching
- Repository links: the source of edit, history, and issue actions
1 - Configuration
OINK follows a “native first” configuration model. Site identity, languages, menus, outputs, taxonomies, markup, and modules stay in their Hugo-defined locations. Existing Docsy parameters remain where their semantics are useful. OINK adds only focused choices for behavior that cannot be inferred.
Configuration rules
- Prefer Hugo configuration over a theme-specific duplicate.
- Prefer an established Docsy parameter over an OINK synonym.
- Put brand, content, repository, and UI choices in their semantic locations.
- Keep internal vendor paths and template composition out of the public API.
- Fail early for invalid values or a missing required endpoint.
There is no oink.enabled flag and no params.oink.* tree. Adding either would
create a second theme mode and make every fix, test, and document ambiguous.
A complete baseline
This example makes English primary and Simplified Chinese secondary:
The module version is pinned in the site’s go.mod. A conventional theme
checkout can instead use theme: oink with the repository under themes/oink/.
Languages
defaultContentLanguage determines the unprefixed primary site. Language
weight controls the visible order. label is the language’s self-name, and
locale supplies the full HTML and SEO locale. Add languageDirection: rtl to
an RTL language.
File naming
For the colocated model used by this site:
Files with the same base name are translations. Keep their logical page identity aligned. OINK reads Hugo’s translation relationships; it does not guess from arbitrary URL patterns.
Selector states
The selector needs no mode parameter. It is hidden for one configured language. With two or more, clicking the language icon advances to the next language by weight; hovering for half a second or focusing it opens the complete menu.
If the current page lacks a target translation, the target-language home page is used. Do not add dead page-shaped URLs merely to keep the selector on the same path.
Brand and repository
Set the site and per-language title and description. params.logo can point
to a Hugo Asset or a path under static/. Keep favicons and social images in
the documented asset locations.
Repository metadata drives “edit this page,” issue, and last-modified links:
github_project_repo defaults to github_repo where supported. github_subdir
is the content site’s path inside a monorepo. Keep github_branch resolvable; a
display version is not necessarily a Git ref.
Use params.wordmark for a horizontal brand asset that should appear in the
navbar, the sidebar drawer, and the footer. It accepts the same asset and
static/ paths as params.logo. The compact navbar falls back to params.logo
on its own, because a wordmark would consume the whole row; if wordmark is
absent entirely, OINK keeps the existing logo-and-title treatment:
Navigation and layout
OINK retains Docsy menus and UI parameters and adds focused shell controls:
navbar_enabled and footer_style decide whether each page carries the site
navbar and which footer shape it uses. Both default to on (true and fat),
apply to every layout, and can be overridden per section through a cascade or
per page in front matter; an unknown footer_style fails the build. See
Navigation and menus and
Site footer.
navbar_accordion_single_open is retired. There is no separate mobilemenu to accordion — below lg the navbar keeps every item visible as an icon.
page_width accepts normal, wide, or full and can be overridden in page
front matter. Sidebar minimum and maximum values are pixels used to clamp the
desktop drag resizer. sidebar_item_overflow: wrap wraps long labels; other
values retain the compact ellipsis behavior.
quick_links names top-level page references shown by the shell. Define their
translated names in each language’s main menu. taxonomy_icons sets the
right-rail group icon per plural taxonomy name, defaulting to a folder for
categories, tags for tags, and a generic shape elsewhere.
The page actions are a split button in the breadcrumb row: the primary half
copies the page’s Markdown, and its menu keeps Copy Markdown, the assistant
links, View markdown, View edit history, Edit this page, Create child page, the
documentation and project issues, and Print entire section reachable at every
viewport width. Built-in Open in ChatGPT / Claude actions are disabled by
default. Set assistant_links: true to show them on file-backed pages; when a
reader activates one, the full current URL — including its query string and
fragment — leaves the site inside a localized prompt. Oink does not upload the
page body. Avoid secrets in URLs and disclose this third-party boundary. A page
can override the site policy with boolean assistant_links front matter.
View edit history appears when github_repo can resolve the same repository
path used by Edit this page. links is empty by default. Additional custom
links accept URL-encoded {url}, {title}, and {markdown_url} placeholders:
Homepage data
Homepage content lives in data/home/<language>.yaml, with English used as the
fallback. Each language file contains named data blocks and an optional
sections list that composes those blocks into the exact landing-page order.
The site footer is no longer part of that file — it now renders on every layout
and reads data/footer/<language>.yaml. See
Site footer.
Compose sections
A string entry uses the same value as its section type and data key. A map entry
can select a built-in type, read a differently named key, set a stable id,
or temporarily set enabled: false:
Map entries may also carry their content in data, which is useful for a short
one-off block. Reuse a built-in type with different keys when two sections need
the same presentation. A site-owned layout can name an explicit partial, but
that is a custom template contract rather than portable homepage data.
If sections is absent, OINK preserves the 0.1.x order by rendering the blocks
that exist among hero, metrics, capabilities, principles, and cta.
Adding sections opts into explicit composition; omitted blocks then stay out
of the page even if their data remains in the file.
Built-in sections
OINK 0.4.0 provides 21 section types:
| Type | Use it for |
|---|---|
hero |
Primary message, actions, and theme-aware artwork |
metrics |
Compact facts, numbers, links, and supporting text |
capabilities |
Alternating feature narratives and specialist visual panels |
principles |
Numbered product or operating principles |
cards |
Generic feature, benefit, service, or path collections |
logo-wall |
Tools, integrations, partners, or project lineage |
gallery |
Screenshots or icon-led examples with badges and actions |
testimonials |
Quotations with optional attribution and source links |
contributors |
People, roles, avatars, and profile links |
faq |
Native disclosure controls with Markdown answers |
markdown |
Free-form prose when no collection layout is appropriate |
cta |
One final action or a compact group of actions |
pricing |
Product tiers, prices, features, and calls to action |
pricing-compare |
Feature comparison matrices across pricing tiers |
command-box |
A focused command with Copy support and an optional note |
steps |
Ordered procedures with optional command examples |
timeline |
Dated milestones, roadmaps, and release histories |
code-plate |
Static code or line arrays in a presentation panel |
case-study |
Evidence-led stories with metrics, quotation, and source |
download |
Validated rolling and pinned download channels |
bar-chart |
Comparable non-negative values rendered without chart JS |
The homepage and regular layout: landing pages use the same registry. See
Landing pages for data resolution, the nine
scenario-oriented section contracts, no-JavaScript behavior, and output rules.
Common collection blocks accept eyebrow, title, desc or text, columns,
and items. Item fields vary by presentation but consistently use title or
name, desc or text, icon, image, url, and external. Ordinary text
fields render Markdown. Keep internal URLs relative to the language root; set
external: true for links that should open as external navigation.
Hero media
Every block is optional, so a site can keep a short landing page without copying the layout. For example:
The optional hero.image block adds a theme-aware visual on the right. Set
light and dark to files under the site’s static/ directory; the active
image follows the color-theme selector. If only src, light, or dark is
provided, OINK uses that image for both themes. A string value is also accepted
as a shared image. Omit image to keep the text-only Hero.
Below the landing sections, every page ends with the same site footer: the
column grid when footer_style is fat, then the bottom bar. Its left side
retains Docsy’s params.copyright API: use a Markdown string or a map with
authors, from_year, and to_year; when omitted, Hugo’s top-level
copyright value is rendered as-is. OINK’s params.footer_center_info accepts
inline Markdown, defaults to Powered by Oink, and can be set to an empty
string to hide the center region. The right side contains the language controls.
A site that still keeps a footer block in data/home/<language>.yaml is read
as before, but that data now feeds the footer on every page rather than the
homepage alone. Move it to data/footer/<language>.yaml when convenient.
Linked capability boards
A capability row can turn its component board into a compact navigator. Add a
url to each linked item, name the region with aria_label, and choose one to
four columns. Items without a URL remain decorative, so existing boards keep
their current behavior:
Search
The project site enables local search by default:
offlineSearchIndex controls how much text is downloadable in each language’s
index. The scopes are cumulative: title indexes titles and taxonomy metadata;
heading adds page headings; summary adds descriptions or summaries; and
content also adds the complete body. content is the compatibility default,
while summary is a smaller starting point for most documentation sites.
offlineSearchMaxResults applies to both Lunr and the CJK substring fallback.
Each language receives a distinct index. Hosted alternatives remain supported through their established Docsy settings, but enabling them intentionally adds an external service boundary. Do not configure several competing search providers without also deciding which UI should be visible.
Content runtimes
Browser-only runtimes
Mermaid and KaTeX are detected from content. Enable Markmap at the site level:
Swagger UI, Redoc, Asciinema, ECharts, Infographic, and carousel assets load when their shortcodes appear. Their local runtime paths are internal and should not be configured.
Service endpoints
PlantUML and Diagrams.net require explicit endpoints:
Leave the features disabled in an air-gap site unless those URLs are reachable inside the isolated network.
Page-level overrides
Hugo’s .Param lookup allows many site parameters to be overridden in front
matter:
navbar_enabled and footer_style are read from front matter directly, not
from a ui block, so a section can set them once in its cascade.
Use overrides for real content differences, not to reconstruct a separate visual system page by page.
Avoid false configuration
Do not expose:
- a switch between “Docsy” and “OINK” shells;
- paths to vendored JavaScript, CSS, fonts, or internal partials;
- duplicated language or repository values under a brand namespace;
- toggles that merely select one of two copied implementations.
If a site needs a custom product matrix or portal, keep that component in the site and use a narrow hook or shortcode. A local business feature is clearer than a misleading global theme option.
Validate changes
After changing configuration:
- build with the minimum supported Hugo Extended version and the current validation version;
- test every configured language and one page without a translation;
- verify root and subpath
baseURLoutput if both are supported; - inspect local search and optional runtime requests;
- check the desktop and mobile shell, dark and light themes, and print output.
An accepted configuration is one that builds and behaves correctly, not merely one that parses as YAML.
2 - Navigation and menus
OINK combines Hugo’s content tree and menu model with a documentation workspace: a site navbar on every layout, a collapsible and resizable section sidebar, a collapsible page outline in the right rail, and a site footer. The same structure works for English, Chinese, and right-to-left languages.
Site navbar
The navbar is built from Hugo’s main menu plus OINK-generated controls: the
version selector, the language selector, the color-mode control, search, and the
project repository link. It renders on every layout — landing pages, docs,
blog, Swagger, and taxonomy pages alike — so the same site-level navigation is
one click away from anywhere.
Turning the navbar off
navbar_enabled defaults to true. Turn the navbar off for the whole site, for
one section through a front-matter cascade, or for a single page:
Front matter wins over the site parameter, and an explicit false is honored at
every level. Without the navbar OINK restores the chrome the navbar replaced:
the mobile subnav, the sidebar’s brand and search rows, the utility buttons on
the TOC rail, and the sidebar footer utilities. Use it for a page that has to
own the full viewport, not as a general layout preference.
Two states, no separate mobile menu
The navbar has exactly two states:
| Width | State |
|---|---|
lg and up |
Full: brand, menu labels, and the utility controls |
Below lg |
Compact: the logo, then every item as a right-aligned icon |
Compact is not a reduced menu. Menu entries keep their icons, search stays a
magnifier, and the version, language, and theme controls stay where they are —
nothing collapses into a hamburger, because there is no separate mobile menu to
collapse into. The one width-gated control is on shell pages below md, where
an extra icon opens the sidebar drawer.
navbar_accordion_single_open is retired. The parameter is ignored;remove it from existing configuration.
Adding main menu entries
Define a menu entry in page front matter:
Lower weights appear first. A site-level external link is similar:
Use an identifier for configuration that refers to a menu item. Localize
name or linkTitle in language configuration, but keep identifiers stable.
Nested dropdowns
Top-level menus support one level of dropdown. Use Hugo’s parent to
establish the relationship:
A child’s params.description renders under its title in the dropdown, helping
a reader decide where to go.
One interaction detail matters: the parent is a plain link. Its panel opens on hover and on keyboard focus, and clicking or pressing Enter navigates to the parent page. There is no disclosure caret to press, and no state in which the parent page is captured by its own menu. Escape closes the panel and leaves focus on the link; touch users navigate straight to the parent page, which carries the same links in its own content.
warning and degrade to static group headings — they never create a third-level flyout. Deep information architecture belongs in the content sidebar, not the top menu.
Version menu
The selector appears when params.versions is configured. It is a branch icon
that opens its list on hover or keyboard focus, sharing one popover style with
the language and theme controls. Each entry can be a heading, separator,
release, development build, or site variant:
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, the
language icon advances to the next language by weight on click, while hovering
for half a second or focusing it 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 shows a theme control. Clicking it toggles light and dark; hovering or focusing it opens a System / Light / Dark picker, where System follows the reader’s operating system. See Light/dark-mode menu.
Search box
Search is a magnifier icon in the navbar. It opens the Command Palette, as do
Cmd/Ctrl-K and, outside editable controls, /. The icon appears when offline
search is enabled; with the navbar disabled the search row returns to the top of
the sidebar. 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:
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".
Below lg the icon is all that remains of a menu entry, so give every top-level
entry a pre icon. An entry without one has nothing to show in the compact
state.
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:
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:
sidebar_menu_compactshows the active branch and nearby entries.sidebar_menu_foldablelets readers expand or collapse sections. Blog sections start expanded; setsidebar_expanded: falsein a section’s front matter to start it collapsed.sidebar_menu_truncatelimits entries and emits a build warning when the limit is too small.sidebar_cache_limitenables shared navigation markup above the configured site size.sidebar_width_minandsidebar_width_maxclamp the desktop drag-resizer.sidebar_item_overflowisellipsisby default; usewrapfor 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:
Use icons consistently across siblings. They are secondary cues, not a replacement for text labels.
Sidebar icon density
An icon on every leaf page produces noticeable visual noise. Control the density
with sidebar_icon_policy:
| Value | Effect |
|---|---|
all |
Every eligible sidebar entry shows its icon |
groups |
Only roots and nodes with children show icons; plain leaves do not |
none |
Sidebar entry icons are omitted entirely |
The compatibility default for an unset value is all. New sites should set
groups explicitly — it keeps the semantic marker on groups while removing
the noise at leaf level. This site uses that setting.
An invalid value warns and falls back to all.
Adding manual links to the side nav
Create a placeholder page at the desired position:
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.
Section as sidebar root
The sidebar tree is rooted at the reader’s current top-level section, and the row above the tree names that root. A large sub-tree — a versioned API reference, a separate handbook — can become a root of its own so readers can switch into it without leaving the section:
Then set a descendant section’s _index.md:
self applies the root to the section index and descendants; children keeps
the index in the parent tree but roots its descendants. Rooted sections can
nest, but redundant or invalid values produce build warnings.
The switcher is scoped to the current top-level section. Its entries are
that section itself, which is the default, plus every descendant that sets
sidebar_root_for: self. Sibling top-level sections are not listed — moving
between Docs and Blog is the navbar’s job. A section with no switchable
descendant therefore shows no dropdown at all: the row is a plain, unboxed link
to the section landing page, flush with the tree’s top-level rows.
Taxonomy term pages have no content ancestry, so a term adopts the top section its members share. Following a tag from a docs page keeps the docs tree and the docs root link instead of falling back to the site-wide tree; a term whose members span several sections shows no root row.
Table of contents (TOC)
Hugo builds the right-side page outline from Markdown headings. OINK renders it as the first group in a fixed right rail, followed by the taxonomy clouds for the current section. Readers can collapse the rail; 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:
Configure which heading levels Hugo includes:
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.
Right-rail groups
Every group in the rail uses the same header row: an icon, a title, and a chevron, with the whole row highlighting as one item. The outline group is titled Content, and its icon is a three-line glyph that collapses the rail rather than a decoration. In the sidebar drawer the same group keeps a static three-line icon so it reads like the taxonomy heads beside it.
Taxonomy group icons are configurable by plural taxonomy name:
categories defaults to a folder and tags to tags; any other taxonomy gets a
generic shapes glyph until it is named here. See
Taxonomy support for how the clouds
themselves are scoped.
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:
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.
Breadcrumb navigation
Breadcrumbs are shown above ordinary content pages and in taxonomy results, and that row also carries the page actions. Top-level section pages keep their one-crumb breadcrumb so the row stays anchored at every depth. Disable breadcrumbs globally:
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.
Page actions
The page actions are an icon-only split button at the end of the breadcrumb row. The primary half copies the page’s Markdown and flips to a green check on success; the caret opens a menu of ten actions in two halves. The reading half takes the page somewhere else:
- Copy Markdown
- Open in ChatGPT
- Open in Claude
- View markdown
- View edit history
A separator follows, then the acting half, which changes or produces something:
- Edit this page
- Create child page
- Create docs issue
- Create project issue
- Print entire section
Configured page_context_menu.links come last, after a second separator. Every
entry appears only when it can resolve: the Markdown actions need the markdown
output format, the repository actions need github_repo, the project issue
needs github_project_repo, and the assistant actions need
params.ui.page_context_menu.assistant_links.
On the blog root and its first-level sections the primary half is the RSS link instead of the copy control, and the menu still offers Copy Markdown. Blog leaf pages carry no feed icon. A page with no Markdown output drops the primary half and renders a labeled Actions button instead.
create_child_page, create_project_issue, and print_section are first-class
registry actions, so they appear in the
Command Palette too. The page-level
print action is retired; readers use the browser’s own Cmd/Ctrl+P.
Site footer
The footer renders on every layout and has three shapes, selected with
footer_style:
| Value | Renders |
|---|---|
fat |
The column grid above the copyright line (the default) |
slim |
The copyright line only |
none |
No footer at all |
Front matter — including a section cascade — overrides the site value:
An unrecognized value fails the build instead of falling back silently.
Fat-footer data
The column grid reads data/footer/<language>.yaml, or data/footer.yaml on a
single-language site:
brand.name and brand.logo fall back to the site’s own brand name, logo, and
wordmark. tagline and slogan render Markdown. Internal url values resolve
against the language root; external: true opens the link in a new tab with
rel="noopener noreferrer". The grid’s track count follows the number of
columns in the data.
A fat footer with no data degrades to slim, so a site can keep the default
while it writes the columns.
footer block in data/home/<language>.yaml is still read as afallback. It used to render on the homepage only and now applies site-wide, so check that the columns still make sense from a deep documentation page before keeping the legacy location.
Heading self links
Enable OINK’s heading render hook in a consuming site:
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:
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-offsetto 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.
3 - Languages
OINK uses Hugo’s multilingual page model directly and introduces no
site-specific domain convention or template assumption. This site treats English
as the primary language and Simplified Chinese (zh) as the second.
Configure languages
-
label,string, required The name shown in the language selector, written in that language —
简体中文, notChinese.-
locale,string The standard language tag used for
<html lang>,hreflangalternates, and Open Graph metadata.-
weight,integer Sets both the language order and the selector’s cycle order; lower comes first.
-
title,string The site title in that language.
-
params.*,map Language-level parameters override the global value of the same name; anything undefined is inherited. Date formats usually need a per-language value.
When menu labels differ by language, define menus under each language.
Organize translations
A translation sits beside its source in the same directory, distinguished by a filename suffix:
-
content/docs
- install.md
- install.zh.md
The shared base filename is what makes Hugo treat them as one page in two languages.
Keep identical: dates, weights, aliases, page resources, and every piece of metadata that affects routing.
Translate: front matter title and description, summaries, menu labels,
tags, image alt text, callouts, and visible shortcode parameters.
Do not translate: commands, identifiers, configuration keys, filenames, URLs, and product names.
per-language contentDir model. Do not mix the two layouts — pick one,
write it into your conventions, and verify that Hugo links the translations.
Stable heading anchors
This is where multilingual documentation most often breaks. Hugo derives heading
IDs from heading text, so a Chinese heading produces a Chinese ID and
/docs/page/#install and /zh/docs/page/#安装 become two unrelated anchors.
Write the source ID explicitly in the translation:
When translating an existing page, take the ID from the rendered English HTML. Do not guess from the heading text — headings containing shortcodes or inline code often generate something other than what you expect.
This site enforces identical heading count, order, and IDs with a script:
Language selector behavior
The selector reads each page’s .Translations:
- the target language has a translation → it links straight to that page;
- the target language has none → it falls back to that language’s home page.
The fallback is deliberate, not a defect. Sending a reader to a URL that does not exist would be worse.
Search and languages
With offlineSearch: true, each language gets its own index:
A reader searching from a Chinese page matches only Chinese content.
Chinese queries use the theme’s CJK substring fallback: Lunr cannot tokenize Chinese reliably, so the Command Palette switches to substring matching when it detects CJK characters. Both paths apply the same ranking boost.
Right-to-left languages
Declare the writing direction on the language:
OINK loads Bootstrap’s RTL stylesheet, and the theme’s own CSS uses logical
properties (margin-inline-start rather than margin-left), so mirroring is
automatic.
Site-authored CSS should use logical properties too, or it will break under RTL.
UI translations
The theme ships interface strings for 32 locales. English, Simplified Chinese
(zh-cn and generic zh), and Traditional Chinese (zh-tw) are fully
reviewed; the rest keep their inherited Docsy translations, and OINK-only labels
currently fall back to English.
To override one string, create a file of the same name under the site’s i18n/:
Translation checklist
- every
page.mdhas a matchingpage.zh.md - Chinese headings carry explicit IDs matching the rendered English IDs
- routing-affecting front matter is consistent
- commands, configuration keys, and URLs are untranslated
- the language selector is verified on pages with and without translations
- search returns results in both languages
Next steps
- Versions: combining languages with versions
- Navigation: per-language menus
4 - Versions
When a product has several supported releases, the documentation usually follows. OINK provides two things: a version switcher and an archived version banner.
How each version is deployed is up to you — commonly one subdomain or subpath per version, each built separately.
Version menu
List the versions that should appear in the menu under params.versions:
-
version_menu,string The label on the menu button, usually the current version.
-
versions[].version,string, required The version identifier shown on the menu entry.
-
versions[].url,string, required That version’s documentation address. An entry with no URL renders as unavailable.
-
version_menu_pagelinks,boolean, default:false Whether to append the current page path to the target version’s URL.
Insert a separator with - name: '---' to divide supported from historical
releases:
The page-level switching trade-off
version_menu_pagelinks: true appends the current page path to the target
version’s URL, so a reader switching versions stays on the same document.
The cost is that the target version may not have that page. Documentation structure evolves between releases, an older version may not contain a newly written page, and the reader lands on a 404.
pagelinks: false on an individual entry overrides the global setting so that
version only ever receives its home page.
pagelinks when structure is broadly stable across versions.When it is not, leaving it off is better: an extra click beats a 404.
Archived version banner
On a site for a release you no longer maintain, say so explicitly:
-
archived_version,boolean, default:false When
true, shows an archive notice at the top of every page.-
version,string The version shown in the banner.
-
url_latest_version,string The current version’s address; the banner links to it.
The banner text is localized with the site language; you do not write it.
Deployment layout
Two common arrangements:
| Layout | baseURL |
Character |
|---|---|---|
| Subdomain | https://v1-9.docs.example.com/ |
Fully independent versions |
| Subpath | https://docs.example.com/v1.9/ |
One domain; needs path routing |
baseURL must include that path, orthe search index, page actions, and asset links all point to the wrong place. This is the most common subpath failure.
Each version is built independently: check out the content from its branch
or tag, build with that version’s own hugo.yaml, and publish the output to the
matching address. OINK does not build several versions in one pass.
Next steps
- Languages: combining languages with versions
- Deployment: publishing each version
5 - Repository links and page information
OINK’s documentation and blog layouts can show links to the current page’s source repository. They live in the page actions menu at the end of the breadcrumb row:
- View markdown opens the generated Markdown alternate when that output is enabled.
- View edit history opens the source file’s commit history.
- 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.mdtemplate. - 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.
Link configuration
A typical site configuration is:
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 edit, history, child-page, and documentation-issue links:
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:
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:
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:
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:
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:
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:
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)
github_url is deprecated. Use
path_base_for_github_subdir and the
repository parameters for new content.
A legacy page can set a complete custom edit URL in front matter:
Pages using this value expose Edit this page but not View edit history: the opaque URL has no repository path from which OINK can derive a history destination. A site-specific template override is preferable when the destination is not GitHub-compatible.
Disabling links
Every entry in the menu carries a stable action ID in data-oink-action:
| Link | Action ID |
|---|---|
| View generated source | view_markdown |
| View edit history | view_history |
| Edit this page | edit_page |
| Create child page | create_child_page |
| Create documentation issue | create_issue |
| Create project issue | create_project_issue |
Hide an action in assets/scss/_styles_project.scss when the destination does
not support it:
The same IDs name the actions in the Command Palette, so hiding the menu entry alone leaves the command reachable there.
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:
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.