Skip to content

Configuration

The one place site parameters are defined — every key the theme reads, with its type, default and the guide that covers it.

This is the single home of site parameters. Every key the theme reads has a row in one of the tables below, giving its type, default and a one-line description, and linking to the guide that covers it. The guides give pasteable snippets and never repeat the definitions. Page-level parameters (front matter) are in Page parameters.

The tables are grouped by function, one ## each, and the anchors are referenceable — for example /docs/customize/config/#sidebar. An empty default column means the theme has no default: leave the key out and the feature is off.

The layers of hugo.yml

An OINK site’s configuration has four kinds of key, and which layer you change depends on what you are changing:

Layer Examples Who defines it
Hugo’s own top-level keys baseURL title languages markup outputs taxonomies module Hugo itself; the behaviour is on gohugo.io
Top-level params logo offline_search github_repo version page_width comments Site-level options the theme reads
params.ui.* navbar_enabled sidebar_width_min typography pager_types The shell, navigation and reading interface
params.<runtime> mermaid plantuml drawio markmap Each content runtime’s own switch and endpoint

A minimal working configuration needs only the first two layers:

hugo.yml
title: Product Docs
baseURL: https://docs.example.com/
defaultContentLanguage: en
enableGitInfo: true

module:
  imports:
    - path: github.com/pgsty/oink
  hugoVersion:
    extended: true
    min: 0.160.1

params:
  offline_search: true
  github_repo: https://github.com/example/product-docs

Configuration principles

  • The theme’s defaults are conservative; write only the keys you change. Interactive features (local search, image zoom, comments, feedback, the light/dark menu) are off by default, because the theme does not make policy for a site. Trimming a “complete configuration” leaves behind keys you never needed more readily than adding them as you go.

  • There is no theme master switch. There is no oink.enabled, no params.oink.* namespace, and no option that swaps between a “Docsy shell” and an “OINK shell”. A switch you cannot find on this page does not exist.

  • An invalid value warns and falls back to the documented default. params.ui.typography: solarized reports invalid params.ui.typography "solarized" (allowed: technical | system) -- using "technical" and the site still builds; footer_style: thin, page_width: huge and section_index: grid behave the same way. One typo therefore degrades one setting instead of serving HTTP 500 on every URL under hugo server. It cannot ship silently either: every publishing gate builds with --panicOnWarning, which turns the warning back into a hard failure.

  • One warning keeps the value instead of dropping it. A theme_color the theme reads as below AA body text (4.5:1) against its own canvas still ships — a custom canvas or a brand mandate is the author’s call — but says so, and prints the ignoreLogs id that silences it. Treat it as advice, not a rejection: the fix is either a darker color or one line of configuration, and the publishing gate stops the build until you choose. Only an unparseable hex is dropped outright, and that one falls back to the default palette like every other invalid value.

  • The theme itself never stops the build. Its templates contain no errorf at all: every invalid value takes the warn-and-fall-back path above. A feature needing an external endpoint — PlantUML, Draw.io, Algolia — warns and stays off when the endpoint is missing, because the theme never connects to a public service on your behalf. An incomplete upstream attribution warns and omits the whole notice, because a partial one reads exactly like a complete one. What does stop a build comes from Hugo rather than the theme: a content reference that resolves to nothing, and a Hugo older than module.hugoVersion.min.

Page-level override precedence

Hugo’s .Param lookup lets most parameters be overridden per page, highest precedence first:

  1. The page’s own front matter;
  2. cascade in an ancestor section’s _index.md (nearer wins);
  3. Site params.

Drop the ui. prefix when writing it in front matter. The site’s params.ui.scroll_spy is simply scroll_spy on a page. A ui: block in front matter is read by nobody and reported by nobody, so a setting that seems to have no effect is worth checking against Page parameters first.

content/docs/wide-reference.md
---
title: Wide reference
page_width: wide
navbar_enabled: false
footer_style: slim
scroll_spy: true
---

A cascade sets a whole subtree at once:

content/docs/_index.md
---
title: Docs
cascade:
  type: docs
  footer_style: slim
  feedback: true
---

Overrides are for real differences in content. Rebuilding a visual system page by page tends to fall out of step at the next theme upgrade.

The three Goldmark prerequisites

Hugo does not merge a theme module’s markup configuration into the site, so these three must be in the site’s own hugo.yml, or attribute lines, component HTML and mathematics all stop working:

hugo.yml
markup:
  goldmark:
    parser:
      # block images may carry an attribute line ({caption=…}, numbered figures)
      wrapStandAloneImageWithinParagraph: false
      attribute:
        block: true
    renderer:
      # HTML emitted by `{{% … %}}` shortcodes has to survive
      unsafe: true
    extensions:
      passthrough:
        enable: true
        delimiters:
          block: [['\[', '\]'], ['$$', '$$']]
          inline: [['\(', '\)']]
  highlight:
    # class-based highlighting, so light and dark can each have a palette
    noClasses: false
  tableOfContents:
    endLevel: 4

Without attribute.block, {.fields}, {.steps} and {caption=…} render as literal text; without passthrough, \(x\) never becomes a formula; without unsafe, the structure of steps and cards is escaped away.

renderer.unsafe: true also lets raw HTML in Markdown through. It is meant for trusted authors, not as a submission filter. Where content comes from untrusted sources, the review belongs in the contribution process.

Site identity and brand

Hugo’s own top-level keys:

title , string
Site name, shown in the navbar, <title> and the footer
baseURL , string
The production domain; include the path segment for a subpath deployment
enableGitInfo , boolean , defaultfalse
Required before “last modified” and commit information exist
enableRobotsTXT , boolean , defaultfalse
Generates robots.txt
enableEmoji , boolean , defaultfalse
Allows :smile: shortcodes

Theme parameters:

params.wordmark , string
Horizontal wordmark; when set, the navbar uses it instead of “icon + site name”
params.description , string
Site description, the meta fallback when a page has no description
params.author , string or map
The RSS author; a map takes name and email
params.ui.theme_color , string
#rgb/#rrggbb hex tinting the shell’s accent grounds; prose links and inline code are unaffected — see Brand and appearance
params.ui.theme_color_dark , string , defaultderived
The dark half of the accent; omitted, it is derived from theme_color until it clears AA on the dark canvas

There is no favicon parameter: the theme scans static/ for conventional names (favicon.ico, favicon.svg, favicon-NxN.png, apple-touch-icon.png, apple-touch-icon-NxN.png) — see Brand and appearance.

Shell types and section roots

The shell follows the page type, not the path. Documentation can live in any directory, with a cascade giving it type: docs.

params.ui.shell_types , list , default[docs, book, blog, swagger]
Which types use the reading shell with a sidebar — see Layouts and page types
params.ui.docs_section , string , defaultdocs
The documentation section’s root directory name, used for navigation resolution only
params.ui.blog_section , string , defaultblog
The blog section’s root directory name
params.ui.docs_sidebar_root , enum , defaultsection
With section, a docs page’s sidebar roots at the documentation section; with home, at the site home. An invalid value warns and falls back
params.ui.sidebar_root_enabled , boolean , defaulttrue
Allows a subsection to become its own sidebar tree with sidebar_root_for: self
params.ui.sidebar_root_menu , boolean , defaulttrue
Shows the section switcher above the sidebar; it degrades to a plain link when there is only one entry
params.ui.section_index , enum , defaultlist
Child list style on a section index: list or cards, overridable per section
params.ui.section_index_columns , integer , default2
Column count when section_index: cards

Blog

Seven keys shape a blog section. They apply to the section named by params.ui.blog_section, and each can be overridden per section through front matter or a cascade on the blog root.

params.ui.blog_index , enum , defaultlist
The blog section’s list page: list is the row list, cards a grid of content cards with a 16:9 lead image, the date and section line, and a three-line summary, table one compact row per post — the whole section at once, with no year groups and no pagination. Year grouping, pagination and manual_link behave the same in list and cards
params.ui.blog_index_columns , integer , default3
Column count when blog_index: cards; two between the md and xl breakpoints, one below md, whatever this says
params.ui.blog_index_size , integer , default12
Posts per page on a list or cards index; the table form always shows everything. Twelve divides by two, three and four, so no card row is left short
params.ui.blog_index_toggle , boolean , defaultfalse
Lets a reader cycle the index through list, cards and table from the index toolbar. Off by default, because it puts all three forms in the document — the hidden ones load no images, but their markup is real
params.ui.toc_style , enum , defaultfixed
The right rail’s presentation: fixed is a panel pinned to the viewport, flow a wider panel in the content flow that starts where the article starts and pins only on scroll
params.ui.toc_taxonomies , boolean , defaulttrue
Taxonomy term clouds on the right rail. A rail left with neither a table of contents nor clouds renders nothing at all

Article authorship and series are taxonomies rather than parameters — see Taxonomies and Writing a blog.

params.ui.navbar_enabled , boolean , defaulttrue
Whether the site navbar renders; overridable with a top-level navbar_enabled on a page — see Navigation and menus
params.ui.navbar_autohide , boolean , defaultfalse
The navbar retracts above the viewport and returns when the pointer enters the wake zone; inactive below 768px and on coarse pointers
params.ui.dark_mode , boolean or map , defaultfalse
true enables both the dark palette and the theme control; for the control alone write dark_mode: { show_menu: true }
params.ui.breadcrumb , boolean , defaulttrue
Breadcrumbs; false turns them off. A top-level section already omits a one-level breadcrumb
params.ui.page_context_menu.enable , boolean , defaulttrue
The page action split button beside the title
params.ui.github_stars , string or number
The star count on the navbar GitHub mark; a local constant, never a request
params.ui.alt_site , map
A sibling-site link shown in the footer of a single-language site; label and an absolute http(s) url are both required

The fat footer’s column data comes from data/footer/<language>.yaml rather than from a parameter — see Navigation and menus.

params.ui.sidebar_menu_compact , boolean , defaulttrue
Expands only the current branch and its neighbours
params.ui.sidebar_menu_foldable , boolean , defaulttrue
Lets the reader expand and collapse sections
params.ui.sidebar_menu_truncate , integer , default2000
Maximum entries rendered in one section; the rest are truncated
params.ui.sidebar_cache_limit , integer , default500
Above this page count the site reuses shared navigation markup, and the browser restores the active state
params.ui.sidebar_width_min , integer , default220
Lower bound in pixels for drag-resizing on the desktop
params.ui.sidebar_width_max , integer , default480
Upper bound in pixels for drag-resizing
params.ui.sidebar_item_overflow , enum , defaultellipsis
ellipsis truncates a long title, wrap wraps it
params.ui.sidebar_icon_policy , enum , defaultall
Icon density: all everywhere, groups only on the root and nodes with children, none nowhere. An invalid value warns and falls back to all
params.ui.sidebar_expand_levels , integer , default2
Tree levels expanded by default
params.ui.sidebar_headings , boolean or integer , defaultfalse
type: book only: expands a heading branch under the current sidebar row; an integer from 2 to 4, and true means 2
params.ui.sidebar_enabled , boolean , defaulttrue
The left sidebar; false turns it off, usually per page rather than per site
params.ui.taxonomy_icons , map
Right-column group icons by taxonomy plural, for example tags: fa-solid fa-tags

How to use the sidebar is in Layouts and page types; the tree itself comes from the shape of content/ — see Organizing content.

Table of contents

The outline’s levels come from Hugo’s own configuration; the theme controls only the tracking behaviour:

markup.tableOfContents.startLevel , integer , default2
Hugo’s own: the highest heading level collected
markup.tableOfContents.endLevel , integer , default3
Hugo’s own: the lowest heading level collected
params.ui.scroll_spy , boolean , defaultfalse
Scroll position tracking; true highlights the active entry

Hide the outline on one page with the front matter notoc: true — see Page parameters.

Pager and page end

The page-end components are in a fixed order — share → feedback → page information → pager → comments — and each has its own switch; backlinks sit in the right rail beside the table of contents.

params.ui.share , list , default[]
Page-end share targets, in the order given, from x bluesky mastodon facebook linkedin reddit hackernews telegram whatsapp line pinterest weibo chatgpt claude email copy. Empty means no bar. Every target is a plain intent link — no SDK, no iframe, no third-party script, no share counts — see Writing a blog. An unknown target warns and is dropped
params.ui.pager_types , list , default[docs, book, blog]
Which types show previous / next; a page opts out with the front matter pager: false. An unknown type warns and is dropped
params.ui.annotation , boolean , defaulttrue
The “last modified” and provenance block at the end of the body; the upstream attribution line is driven by the page’s upstream_link family — see Page parameters
params.ui.translation_notice , language code or false , defaultfalse
The language code of the authoritative version, so a translated page shows a line pointing back at it; a page opts out with translation_notice: false
params.ui.reading_time , boolean , defaultfalse
Shows a reading time under the page title
params.ui.book_draft_banner , boolean , defaultfalse
Adds a banner at the top of a draft Book page

Local search is off by default, and the command palette appears only once it is on (the navbar magnifier, Cmd/Ctrl with K, /, \).

params.offline_search_on_serve , boolean , defaulttrue
Builds the index under hugo server too, so the preview behaves like production; set false on a very large site to speed up local rebuilds
params.offline_search_index , enum , defaultcontent
Index scope, cumulative: title, heading, summary, content. An invalid value warns and uses content
params.offline_search_summary_length , integer , default70
Word cut-off for the summary scope’s excerpt
params.offline_search_max_results , integer , default10
Result cap, bounding both Lunr and the CJK substring fallback
params.ui.command_palette.commands , list , default[]
Custom commands, each with either url or a built-in action — see Command palette
params.gcs_engine_id , string
A Google Programmable Search engine ID; enabling it brings in an external service
params.search.algolia , map
Algolia DocSearch; appId, apiKey and indexName must all be given explicitly, or it warns and DocSearch stays off

A custom command record accepts seven keys only — id, title, description, icon, keywords, url, action — and id must match ^[a-z][a-z0-9_-]*$ and must not collide with a built-in action ID. Per-language titles go under languages.<lang>.params.ui.command_palette.commands.

Keyboard

params.ui.keyboard_nav , boolean , defaulttrue
Single-key navigation (WASD / arrows walk the tree, j/k jump headings, q/e page, palette and shell switches). With false the runtime never enters the bundle — see Keyboard navigation

Image zoom

params.ui.image_zoom , boolean , defaultfalse
Lets body images open full size; a page overrides it with the front matter image_zoom. A non-boolean warns and falls back

Which images become zoom candidates is in Images.

Typography

params.ui.typography , enum , defaulttechnical
technical uses the bundled Inter / Chakra Petch / IBM Plex Mono; system uses the platform stack only and requests no brand font. An invalid value warns and falls back
params.ui.fonts , map
Font-family names for the ui, body, heading, code, display, meta, and print roles. The theme validates names but never loads font files; every list should end in a generic family
params.page_width , enum , defaultnormal
Overall shell width: normal, wide, full; overridable per page
params.reading_width , enum , defaultnormal
Reading measure of a Book page’s body: slim, normal, wide; it does not affect the shell

Use params.ui.fonts when the faces already exist on the reader’s system or the site has declared them with @font-face. Bundling font files and changing lower-level typography still use the SCSS/CSS entry points — see Brand and appearance.

Comments and feedback

params.comments.enable , boolean , defaultfalse
The site-level comment switch; a page overrides it with the front matter comments — see Comments
params.comments.type , string , defaultgiscus
Only giscus actually renders today
params.comments.giscus.repo , string
The GitHub repository hosting the discussions; required
params.comments.giscus.repoId , string
The repository ID; required
params.comments.giscus.category , string
The discussion category name; required
params.comments.giscus.categoryId , string
The discussion category ID; required
params.comments.giscus.mapping , string , defaultpathname
How pages map to discussions
params.comments.giscus.term , string
The discussion title or number when mapping is specific or number; the attribute is omitted when unset
params.comments.giscus.strict , string , default0
Strict title matching
params.comments.giscus.reactionsEnabled , string , default1
Shows reactions on the main post
params.comments.giscus.emitMetadata , string , default0
Sends discussion metadata to the parent page
params.comments.giscus.inputPosition , string , defaulttop
Whether the input box sits above or below the list
params.comments.giscus.theme , string , defaultauto
The giscus theme; auto follows the site’s light/dark state
params.comments.giscus.lightTheme , string , defaultlight
The giscus theme or custom CSS URL used in light mode
params.comments.giscus.darkTheme , string , defaultdark
The giscus theme or custom CSS URL used in dark mode
params.comments.giscus.loading , string , defaultlazy
The iframe loading strategy
params.comments.giscus.lang , string , defaultderived from the site language
The giscus interface language. Unset, a Chinese site resolves zh-CN / zh-TW / zh-HK, other languages take the base language code, and anything giscus does not support falls back to en
params.comments.giscus.ariaLabel , string , defaultComments
The aria-label on the comment container; the default is English, so a multilingual site writes one per language
params.comments.giscus.errorMessage , string , defaultComments could not be loaded.
Text shown when loading fails; the default is English, so a multilingual site writes one per language
params.ui.feedback.enable , boolean , defaultfalse
The two “was this page helpful?” buttons at the page end; there is no backend, and a structured event is recorded when gtag is present
params.ui.feedback.reasons , boolean , defaulttrue
Expands four optional reasons after “no”

Missing any one of the four required giscus values leaves the comment section unrendered: no error, and nothing appears.

Repository links and page information

params.github_repo , string
The content repository URL, resolving “edit this page”, “view history”, “create child page” and “open a documentation issue” — see Repository links and page info
params.github_project_repo , string , defaultgithub_repo
The product repository URL, for “open a project issue” and the navbar GitHub entry
params.github_branch , string , defaultmain
The branch edit links point at
params.github_subdir , string
The content site’s subdirectory inside a monorepo
params.path_base_for_github_subdir , string or map
Source path rewriting; the map form takes from and to
params.github_url , , default
Removed; write params.github_repo. The migration registry that used to name the replacement is gone, so an old key is now simply an unread key
params.ui.lastmod_commit , enum , defaultsubject
What follows “last modified”: subject the commit subject, hash the short hash, none nothing. An invalid value warns and falls back
params.images , string array , default
The site-level social card: fills og:image when a page has no image of its own. Metadata only; never rendered as a list thumbnail
params.upstream_source , string , default
Default data/upstreams record name for pages that declare upstream_link; page front matter can override it
params.upstream_modified , boolean , defaultfalse
Site default for whether attributed material is adapted; a page can override it, and no attribution renders without upstream_link

Content runtimes

Mermaid, KaTeX, ECharts, Infographic, Asciinema, Swagger UI and Redoc are detected from the content and load only where a page uses them, and only in that page’s HTML output; they have no site switch. Only these need a switch or an external endpoint:

params.markmap , boolean , defaultfalse
Enables the mind map fence site-wide — see Markmap
params.mermaid , map
Configuration passed to mermaid.initialize(); keys are lowercase, and dark mode overrides theme automatically
params.plantuml.enable , boolean , defaultfalse
Enables the PlantUML fence — see PlantUML
params.plantuml.svg_image_url , string
The PlantUML service’s SVG endpoint; required when enabled, and its absence warns and leaves PlantUML off
params.plantuml.svg , boolean
Renders inline SVG instead of an <img>
params.drawio.enable , boolean , defaultfalse
Enables the edit button on .drawio.svg images — see Draw.io
params.drawio.drawio_server , string
The Draw.io editor address; required when enabled, and its absence warns and leaves Diagrams.net off
params.highlight_classes , boolean , defaulttrue
Emits Chroma classes for highlighting; false returns to Hugo’s inline styles
params.ui.code_copy , boolean , defaulttrue
The copy button on code blocks; false removes it globally, and a fence’s own copy= still wins

Mathematics needs no parameter, only the passthrough prerequisite.

Output formats

The theme declares its custom output formats but does not enable them for a site: request what you want under outputs. Expensive aggregate and machine-readable outputs remain explicit opt-ins.

hugo.yml
outputs:
  home: [HTML, markdown, LLMS, NAVJSON]
  page: [HTML, markdown]
  section: [HTML, RSS, print, markdown]
Format Output Description
HTML index.html The interactive form; required
markdown index.md Each page’s plain Markdown twin, which “copy Markdown” and “view source” depend on — see AI-agent support
LLMS llms.txt A plain-text format the theme declares, usually attached to home only
LLMSFULL llms-full.txt A top-level section opt-in: the same per-page Markdown concatenated in sidebar reading order, one bundle per language
NAVJSON navigation.json A home opt-in: the sidebar/pager navigation authority serialized once per language, validated by schema/nav.v1.schema.json
print _print/index.html The whole-section print page the theme declares — see Print
BookManifest book.json A Book-root opt-in JSON handoff for the EPUB/PDF packaging tools; it is not itself an ebook
RSS index.xml Hugo’s own; attach it to section so every section has a feed

LLMSFULL and BookManifest are enabled in the relevant top-level section’s front matter rather than globally. NAVJSON belongs on outputs.home. The complete examples and constraints are in AI-agent support and Books.

Two parameters for print output:

params.print.toc , boolean , defaulttrue
Generates a table of contents at the top of the print page; false omits it
params.print.section_break_wordcount , integer , default50
How many words a section needs before it starts a new print page

Languages and versions

Languages are defined with Hugo’s own languages block, and the theme only reads the translation relationships it establishes:

defaultContentLanguage , string , defaulten
The primary language, served without a path prefix
languages.<lang>.label , string
The language’s endonym, shown in the language menu
languages.<lang>.locale , string
The full locale, used for <html lang> and SEO
languages.<lang>.weight , integer
Language order, and the cycle order when clicking the language icon
languages.<lang>.title , string
The site name in that language
languages.<lang>.direction , string , defaultltr
Set rtl for a right-to-left language

Paired files, anchor alignment and fallback for untranslated pages are in Languages.

Version parameters:

params.version , string
The identifier of this site variant, which need not be a Git ref — see Versions
params.version_menu , string , defaultVersion
The version menu’s title
params.versions , list
Version entries: version, url, kind; name: '---' is a divider
params.archived_version , boolean
Shows the “this is an archived version” banner at the top
params.url_latest_version , string
The link to the current version inside that banner
params.time_format_blog , string , default2006-01-02
Blog date format, overridable per language
params.time_format_default , string , default2006-01-02
All other date formats, overridable per language

Miscellaneous

taxonomies , map
Hugo’s own: enables tag: tags / category: categories — see Taxonomies
params.taxonomy.page_header , list
Shows only these taxonomies in a post header; unset shows all
services.googleAnalytics.id , string
Hugo’s own: the analytics script is injected in production builds only — see Analytics and SEO
module.hugoVersion.min , string , default0.160.1
The Hugo floor the theme declares; anything older fails the build
module.hugoVersion.extended , boolean , defaulttrue
Hugo Extended is required (SCSS has to be compiled)

Editor completion via generated schemas

The theme ships two generated JSON Schemas under its schema/ directory: site-params.schema.json for a site’s hugo.yaml and front-matter.schema.json for page front matter. They are projections of the theme’s own hugo.yaml defaults (with the comment documentation as hover text) and its parameter-scan registry; the theme’s CI regenerates them and fails on drift, so they can never disagree with the theme you have pinned.

With the VS Code YAML extension, map the site schema in your settings:

.vscode/settings.json
{
  "yaml.schemas": {
    "https://raw.githubusercontent.com/pgsty/oink/main/schema/site-params.schema.json": "hugo.yaml"
  }
}

Pin the URL to your release tag instead of main to match your go.mod pin. Front matter completion depends on your Markdown tooling; point it at front-matter.schema.json the same way. The front-matter schema deliberately omits type constraints, because keys like share and theme_color accept a bare-boolean opt-out beside their ordinary type.

Verifying a configuration change

Run a strict build after changing configuration:

hugo --printPathWarnings --panicOnWarning

It passes only when the output reads Total in … with no ERROR and no WARN. Common errors and what they mean:

Error fragment Cause
invalid params.ui.typography The presets are technical and system
invalid footer_style … (allowed: fat | slim | none) A bad footer style; the error names the page
invalid page_width … (allowed: normal | wide | full) A bad page width
invalid params.ui.section_index … (allowed: list | cards) A bad section index style
invalid params.offline_search_index The scopes are title, heading, summary, content
params.plantuml.enable requires an explicit params.plantuml.svg_image_url PlantUML enabled with no endpoint
params.drawio.enable requires an explicit params.drawio.drawio_server Draw.io enabled with no server address
params.search.algolia requires explicit appId, apiKey, and indexName All three Algolia values are required
params.ui.image_zoom must be a boolean Written as the string "true"
theme_color … is not a #rgb or #rrggbb hex color The value is not a hex color; the default palette is kept
theme_color … reads at about N:1 against the theme's … canvas Advisory: the color ships, and the message prints the id that silences it
theme_color_dark … has no theme_color to pair with The dark half was set without a valid theme_color; it is ignored and the default palette is kept in both modes
command … must define exactly one of url or action A custom command gave both url and action, or neither
invalid params.ui.sidebar_icon_policy …; using all Only a warning, but the value is misspelled

A configuration change also needs at least three checks: one page in each language, a page with no translation to see the fallback, and the links under the production baseURL (easy to miss on a subpath deployment).

The theme’s declared Hugo floor is 0.160.1. OINK’s continuous test toolchain is pinned to Hugo Extended 0.165.0; configuration changes are tested once with that pinned version instead of against a version matrix:

# require v0.165.0+extended in this output
hugo version
hugo --printPathWarnings --panicOnWarning

The floor is declared in the theme’s hugo.yaml and theme.toml, and a site’s own module.hugoVersion.min should agree with it. It remains a consumer compatibility declaration, not a second routine CI test leg.