This is the multi-page printable view of this section. .
Scenario components
Scenario Components solve a complete publishing job rather than one fragment of a page. Each scenario coordinates content, local data, navigation, runtime loading, accessibility, and non-HTML output behind one strict contract.
They complement the component reference: use that chapter to look up an individual writing primitive, and use this chapter when the job spans multiple pages, files, or output formats.
Choose a scenario
| Scenario | Use it when you need | Primary source of truth |
|---|---|---|
| Sequential reading | A manual, Book, or blog should have a dependable sequence | Sidebar/content tree and page metadata |
| Releases and downloads | Release facts, assets, and install paths must agree | Front matter and data/download/ |
| Landing pages | A product page needs reusable full-width sections | data/landing/ or inline section data |
| Book publishing | Long-form work needs numbering, citations, and print | Existing Book tree and stable page IDs |
Shared guarantees
- Local facts: normal builds do not fetch release state, stars, prices, screenshots, avatars, or other mutable facts from remote APIs.
- Static first: HTML contains the complete content before progressive enhancement; a page receives JavaScript only for the capabilities it uses.
- Strict input: malformed parameters, identifiers, URLs, checksums, or data records fail the build with a useful source position.
- Output aware: HTML, print, Markdown, and RSS either receive a defined representation or deliberately omit interaction-only content.
- One navigation authority: the visible content tree also drives pagers, Book tables of contents, and aggregate print order.
- Language safe: shared facts use documented suffix fallback, while narrative data may be maintained per language.
Before adoption
Pin the minimum release that owns these contracts:
Use Hugo Extended 0.160.1 or newer. Adopt one scenario at a time, build every configured output, and inspect the result in each language. A local build, a public theme tag, a site version pin, and a hosted deployment are separate evidence gates; do not infer one from another.
For an existing Oink site, start with the 0.4.0 upgrade guide.
1 - Sequential reading and mathematics
Oink 0.4.0 gives manuals, Books, and blogs a defined reading sequence. The same release also makes server-rendered mathematics a first-class content path.
Enable or narrow the pager
The pager is enabled by default for the docs, book, and blog content
types. Replace that set explicitly when a site uses only some of them:
Only those three type names are valid. A page or section can opt out with a boolean front matter value:
Interactive HTML renders only the previous or next destination that exists and
adds matching <link rel="prev"> and <link rel="next"> elements to the page
head. Print, Markdown, and RSS contain no pager markup or relations.
Understand reading order
Docs and Books use a pre-order traversal of the same navigation root as the
sidebar: a section index precedes its visible children, and ordinary children
follow weight order. If data/docs_nav.json supplies an explicit tree, that
tree is authoritative for both sidebar and pager.
These visible navigation entries are not destinations:
- pages hidden with
toc_hide; - link-only placeholders using
manualLinkormanualLinkRelref; - non-linking rows marked
sidebar_divider: true.
Blogs preserve Hugo’s section time order. This is deliberately different from the manual tree order.
Manuals normally live below the configured docs section. If manual pages
deliberately live at the content root and /docs/ is only an overview, set:
docs_root accepts only section (the default) or home; an invalid value is
a build error. With home, top-level toc_root: true overview sections remain
outside the manual sequence.
Render delimiter mathematics
Hugo does not merge a theme’s Goldmark configuration into a consuming site, so the site must enable passthrough delimiters:
Oink supplies the passthrough render hook and local KaTeX CSS. Formulae render
server-side as KaTeX and MathML, and only formula pages receive the stylesheet.
math: true by itself does not enable delimiter parsing.
Build a page containing both inline and display delimiters, then inspect the
HTML for MathML rather than literal $$. Long display formulae scroll within
the article column on screen and remain static in print.
Use the display-math escape hatch
When a site cannot enable Goldmark passthrough yet, use the parameter-free display form:
This form is intentionally unnumbered. It creates no anchor, caption, or Book
registry entry and emits a plain $$ block in Markdown and RSS. To create a
numbered, referenceable equation, adopt the
Book equation form and add a quoted
num.
Validate the reading experience
- Compare sidebar order with the
q/eshortcuts and visible pager. - Confirm link-only, divider, and hidden entries are skipped.
- Inspect head relations at the first, middle, and last destination.
- Build from a subpath and confirm pager links remain on the current origin.
- Check that print, Markdown, and RSS omit interaction-only pager markup.
- Test formula pages in both color modes and print, then confirm an ordinary page does not load KaTeX CSS.
See Keyboard navigation for all reading keys.
2 - Releases and downloads
Oink separates immutable release facts from their presentation. A release page owns the version and repository identity; local download data owns distribution channels. Cards, lists, checksum tables, documentation pages, and Landing pages derive from those records instead of copying URLs and commands into several templates.
Define release facts
Add a strict release map to the page front matter:
version and repo are required. Omit tag to derive v{version} and omit
date to use the page date. Optional product, prev, and checksums fields
complete the record. Unknown keys, wrong types, or a repository outside the
owner/name form fail the build.
For a simple GitHub release, the exact tag URL is also accepted as shorthand:
Oink derives repository, release, archive, compare, checksum, and asset links locally. It does not call GitHub during a build and does not claim that a tag or asset exists remotely.
Render the release card
Place the parameter-free shortcode where the fact summary should appear:
The invocation accepts no facts and no parameters; the page front matter is the only authority. HTML receives a semantic link card with no runtime. Print and RSS receive a static link list, and Markdown receives ordinary Markdown links.
Build a release index
A release section can opt into deterministic ordering:
Pages sort by normalized release date descending, then valid SemVer precedence,
then a deterministic lexical fallback. The default is one global sequence.
release_group_by_product: true requires each selected page to define
product. release_products accepts one product or an array, matches the
product string exactly, and filters before sorting. Invalid filters fail instead
of rendering a plausible but empty page.
Publish checksum assets
Write exact sha*sum lines inside release-assets:
Or commit a checksum file as a page or Hugo asset and reference exactly one source:
The parser rejects malformed lines with their line number, mixed algorithms, algorithm/hash-length disagreement, path-like filenames, and ambiguous input sources. HTML links each asset and conditionally loads one local copy runtime; print shows complete hashes without controls; Markdown and RSS emit full-hash tables.
group="auto" groups common platform and architecture names. Use algo only
when declaring the expected checksum algorithm, and base only when the asset
base differs from the URL derived from release facts.
Define download channels once
Create data/download/pig.yaml:
A record needs a string version directly or through params.version, plus a
non-empty channels array. Every channel needs a unique anchor-safe id, one
kind (rolling or pinned), and a localized title.
Shared fields resolve from the exact language suffix, then the primary-language
suffix, then the unsuffixed field. For example, Chinese may resolve
title_zh_cn, then title_zh, then title. Only a pinned channel’s url and
steps[].code may interpolate ${version} or ${tag}. Rolling channels reject
all interpolation so a stable command cannot accidentally pretend to be pinned.
Render downloads
Reference the data key with one positional parameter:
HTML renders a channel index and static-first sections. Code steps reuse Oink’s enhanced code renderer; checksum channels reuse Release Assets. Print expands the same safe content, Markdown emits headings, source fences, and full hashes, and RSS omits the component.
Set published: false before the immutable release exists. Rolling channels
remain usable, while pinned channels show a non-linking pending state, omit
pinned commands, and disable asset links and copy controls. Flip the fact only
after the tag and assets resolve; never paste speculative links into prose.
A Landing page can consume the same record with a download section:
Release checklist
- Confirm the version, tag, previous tag, repository, and date from the source release process.
- Validate every checksum against the published artifact before committing it.
- Build HTML, print, and Markdown; inspect complete hashes outside HTML.
- Test
published: falsebefore publication andtrueonly after the remote tag and assets resolve. - Verify every language and a subpath deployment.
- Treat source completion, theme tag publication, module resolution, consumer pinning, and hosted availability as separate gates.
3 - Landing pages
A Landing page is a regular Hugo content page with a full-width scenario shell. It keeps the site navbar, Command Palette, and configured footer, but removes the docs sidebar and table-of-contents rail. Content remains local and server-rendered; no frontend build or remote fact API is required.
The homepage continues to use data/home/<lang>.yaml, but now shares the same
renderer and section contracts.
Create a Landing page
Create a regular content file and name its local data key:
Put the English and Chinese narrative data in separate files:
A non-home page resolves data in this order:
sectionswritten directly in page front matter;data/landing/<key>/<exact-language>.yaml;- the exact-language entry in
data/landing/<key>.yaml; - the English or unsuffixed local record.
Use per-language files for narrative content. Shared fact fields may use an
exact language suffix, then a primary-language suffix, then an unsuffixed
fallback; language tags normalize - to _. For example, title_zh_cn
precedes title_zh, which precedes title. camelCase suffix aliases are not
accepted.
Compose sections
Each sections entry is either a type string or a map. A map may set type,
read a differently named key, supply a stable id, disable itself with
enabled: false, or carry one-off inline data:
Use canonical hyphenated type names. Underscores in existing homepage data are
normalized for compatibility. Unknown types warn instead of silently
disappearing. A deliberate site-owned partial remains an escape hatch, but it
is a local template contract rather than portable Landing data.
Section registry
Oink 0.4.0 provides 21 canonical section types:
| Type | Use it for |
|---|---|
hero |
Primary message, actions, and theme-aware artwork |
metrics |
Compact facts, numbers, links, and count-up enhancement |
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 or partners in a grid or CSS-only marquee |
gallery |
Screenshots or icon-led examples |
testimonials |
Quotations with optional attribution |
contributors |
People, roles, avatars, and profile links |
faq |
Native disclosures or a static flat question list |
markdown |
Free-form prose |
cta |
One final action or a compact action group |
pricing |
Product tiers, prices, features, and calls to action |
pricing-compare |
Feature comparison matrices across pricing tiers |
command-box |
A focused copyable command and optional note |
steps |
Ordered procedures with optional command examples |
timeline |
Dated milestones, roadmaps, and release histories |
code-plate |
Chroma code or validated line arrays in a presentation panel |
case-study |
Evidence-led stories with metrics, quotation, and source |
download |
One or more validated data/download/ records |
bar-chart |
Comparable non-negative values normalized without chart JS |
The existing
homepage configuration
documents the shared collection and Hero fields. For the nine scenario-oriented
types, begin from a small entry and let strict validation identify a missing or
invalid field. The Oink repository’s
exampleSite data
is the complete executable reference.
Keep facts local
Pricing, stars, screenshots, avatars, quotes, and download state must exist before Hugo starts. Refresh them in a site-owned maintenance or CI job, review the diff, then commit or generate local data. Do not add browser fetches to a section.
Optional local chrome facts are also strict:
landing_search must be a boolean and only exposes the existing local Command
Palette when offlineSearch is also enabled. github_stars is a committed
string or number, not a GitHub API request. alt_site requires a label and an
absolute HTTP(S) URL.
Progressive enhancement and accessibility
HTML sets one page flag and conditionally loads landing.js. That runtime
enhances reveal, count-up, copy, theme-image, and compact-menu behavior; the
server-rendered document remains complete with JavaScript disabled.
Marquee duplication is CSS-only. The duplicate track is hidden from assistive technology and interaction, and a localized checkbox pauses motion without JavaScript. Reduced-motion preferences disable movement and reveal transitions; forced-colors mode preserves controls and state distinctions. The compact menu uses real links and buttons, traps no focus, and does not duplicate the desktop navigation tree.
Output and validation
| Output | Contract |
|---|---|
| HTML | Complete static content, then conditional progressive enhancement |
| Content retained; motion surfaces become static; controls removed | |
| Markdown | Headings, prose, lists, tables, and code without component classes |
| RSS | Landing sections omitted |
Before publication, test with JavaScript disabled, reduced motion, forced colors, keyboard-only input, both color modes, each language, and a subpath base URL. Confirm every internal link and asset retains the deployment prefix. The inherited Docsy block shortcodes remain compatible, but use Landing data for new pages rather than adding another custom HTML layer.
4 - Book publishing
Oink’s Book capability extends the documentation shell. It uses the existing
content tree or data/docs_nav.json, the same breadcrumbs and pager, and the
same output-aware component system. It does not introduce a second chapter
manifest or a parallel navigation implementation.
Create the Book root
A section Book declares its type, requests the outputs it needs, and cascades the type to descendants:
The Book root is always the current first section, even when the site enables a
sidebar root switcher. Its navigation cannot leak into sibling docs or blogs. If
a site overrides params.ui.shell_types, retain book in that list.
Request print explicitly: Oink never adds an expensive aggregate output to a
consumer configuration. For a section Book, the relevant Hugo output kind is
section; use home only when the Book is the site root:
sidebar_headings accepts false, true (level 2), or a maximum heading level
from 2 through 4. It projects the active page’s Hugo fragment tree below the
chapter row. Use explicit heading IDs for anything that will be cited:
Generated heading slugs are convenient navigation, not a durable citation API.
Describe chapters
Chapters may use the established metadata namespace:
book_number appears beside titles in the page, sidebar, and generated Book
table of contents. book_status: draft is a visible editorial label and does
not change Hugo’s publication state. Set book_draft_banner: true to add a
localized page notice as well.
Add numbered components
The numbered forms of fig, tbl, and eq require a quoted num matching
letters, numbers, dots, or hyphens. Their default IDs are fig-<num>,
tbl-<num>, and eq-<num>; set an explicit stable ID when preserving an
existing public anchor.
Figures
New figures should always provide meaningful alt. title is a migration alias
for caption and the two are mutually exclusive. A figure may use src or
inner Markdown content, never both. URLs, class tokens, and positive image
dimensions are validated.
Tables
The component keeps label, Markdown table, caption, and anchor inside one semantic figure. It does not simulate captions with a heading.
Equations
Equation content goes directly through local server-side KaTeX, even if the site
has not enabled Goldmark passthrough. The parameter-free eq form remains an
unnumbered display-math escape hatch and cannot be an xref target.
Duplicate IDs, or two components of the same kind claiming one number with different IDs, are build errors. Captions are plain text; figure and table body content follows the page’s Markdown policy.
Cross-reference safely
Reference a numbered target by kind and number:
Reference a heading on another page with explicit link text:
xref accepts at most one kind (fig, tbl, or eq), plus optional page
and anchor. A kind supplies the localized default label and derives the
default anchor. An anchor-only reference requires inner text. Cross-page lookup
uses Hugo’s current-language page resolution, so source never hard-codes an
/en/ route.
References are order-independent and may appear before their targets. In
whole-Book print, Book-aware xrefs become document-local fragments. Ordinary
Markdown cross-page links intentionally remain site URLs, so use xref for
citations that must work inside the aggregate.
Generate Book indexes
Build a table of contents from the same ordered Book tree:
Depth 1 lists chapters, depth 2 includes nested sections, and depth 3 also
projects each page’s heading tree. drafts=false filters visible editorial
drafts from this generated list only; it does not unpublish their pages.
Generate figure, table, or equation lists:
These shortcodes trigger and aggregate descendant content deterministically, then link to stable target IDs. They do not require a copied registry file.
Publish whole-Book print
The Book root’s print output emits a cover, local table of contents, then the
root and visible descendants in reading order. no_print: true, link-only
nodes, sidebar dividers, and hidden placeholders do not become chapters.
Numbered target IDs remain byte-stable. Page-local Markdown heading IDs receive
a source-page prefix in the aggregate, so repeated anchors such as summary
remain unique; generated heading links are rewritten accordingly. Book ToC,
figure-list, and xref destinations become document-local.
The result is print-oriented HTML, not a network-dependent PDF/EPUB pipeline. Pagination, PDF conversion, and EPUB packaging remain site-owned concerns.
Migrate existing books
Inventory first, transform only unambiguous forms, and stop rather than guessing missing numbers, captions, alternatives, or targets. Preserve existing public IDs independently from display numbers. Run migrations on a branch, retain a machine-readable before/after report, validate every skipped record, and require a zero-change second run.
The Oink v0.4.0 source ships a dry-run-first, idempotent migration tool and observed-site recipes for TPME, DDIA, and pg-internal. Treat those as executable patterns for the named source forms, not universal caption heuristics.
Validate a Book
- Compare the sidebar, pager, generated ToC, and whole-Book chapter order.
- Verify every numbered target ID is unique and every xref reaches a matching kind and number in each language.
- Confirm numbered figure alternatives are meaningful and caption compatible.
- Inspect standalone HTML, Markdown, print, and the whole-Book aggregate.
- Test repeated heading names and cross-chapter citations in aggregate print.
- Run the theme’s
scripts/check-book.pywhen working from a theme checkout, or implement the same rendered-anchor checks in consumer CI.