This is the multi-page printable view of this section. .
Oink Blog
- 1: Introducing the OINK implementation preview
- 2: OINK implementation diary: from copied shells to one theme
1 - Introducing the OINK implementation preview
Today we are publishing the OINK implementation preview: a directly evolved Docsy theme with one canonical product shell, a Hugo-only consumer build, local-first browser dependencies, a general multilingual framework, and a set of reusable content components drawn from PGSTY documentation sites.
This is an implementation and documentation milestone, not a public versioned release. The final public brand, module and package identities, first version, and production Cloudflare Pages deployment remain explicit release gates.
Why OINK exists
Several mature documentation sites had independently copied the same Docsy layouts, navigation, search code, SCSS, JavaScript, and shortcodes. A common fix had to be repeated across repositories, while each site also carried a frontend toolchain and implicit network dependencies that made isolated builds harder than they needed to be.
OINK consolidates the genuinely reusable layer. Product matrices, portals, pricing pages, and other business-specific behavior stay in their own sites. The shared theme owns the documentation shell, browser runtimes, multilingual routing, accessibility behavior, and content-component contracts.
What changes
One product instead of a mode
OINK is not an optional skin. There is no oink.enabled flag, params.oink.*
namespace, or parallel upstream-versus-brand template tree. The implementation
in theme/ is the product.
That decision avoids two visual systems and two test matrices. Native Hugo settings and compatible Docsy parameters keep their established meanings.
Hugo-only consumer builds
A complete consuming site builds with:
Bootstrap, Font Awesome, fonts, search, diagrams, API documentation runtimes, and OINK components are committed with the theme. Node.js, npm, PostCSS, Autoprefixer, and CDN downloads are not consumer requirements.
Repository maintainers still use Node-based tools for tests and vendor refreshes. That maintenance toolchain is deliberately outside the public site-build contract.
Local-first browser behavior
The default starter serves its shell, fonts, icons, search, Mermaid, KaTeX, Markmap, Swagger UI, Redoc, Asciinema, ECharts, and Infographic dependencies from the generated site. Optional runtimes are selected per page and loaded at most once.
PlantUML and Diagrams.net do not receive public service defaults. A site must configure a controlled endpoint, use a pre-rendered result, or make an explicit remote-service choice.
Multilingual infrastructure
Language routing comes from Hugo’s language and translation objects. One configured language hides the selector. With two or more languages, a click advances by configured weight, while a short hover or keyboard focus opens the complete menu. If the current page lacks a translation, the selector goes to the target-language home page instead of a dead path.
The starter and documentation site use English as the primary language and
Simplified Chinese as the second. Every page in the core docs and blog scope has
a colocated .zh.md translation with stable explicit heading IDs.
Reusable components
OINK adds theme-owned Asciinema, ECharts, Infographic, document carousel, details, tabs, cards, navigation cards, document cards, and parameter components. They generate unique instance IDs and load their local assets only when used.
ECharts accepts structured JSON or YAML plus optional JavaScript callbacks
referenced as $fn:name. Callback code runs only on pages that declare it.
What stays familiar
OINK retains Docsy’s content organization, front matter, documentation and blog sections, menus, taxonomies, print output, repository links, common shortcodes, diagrams, API reference features, and extension hooks. Existing sites can remove duplicated common implementations without rewriting ordinary Markdown.
The project also preserves Docsy’s Apache-2.0 history and attribution. A vendor manifest records pinned third-party sources, licenses, artifacts, and checksums.
Try the starter
Install Hugo Extended 0.160.1 or newer, then run from this checkout:
The current validation baseline is Hugo Extended 0.164.0. Open the generated
English and Chinese pages, switch languages, search locally, change color mode,
and visit the component examples.
For a network-isolated transfer, maintainers can create a complete archive:
The archive includes the theme, starter, licenses, upstream record, migration guide, vendor manifest, and a sidecar checksum.
Current validation
The implementation includes automated coverage for:
- minimum-version and current Hugo Extended builds;
- forbidden consumer Node/npm/PostCSS/Autoprefixer paths;
- LTR, RTL, subpath, print, color-mode, and production assets;
- hidden, click-to-cycle, hover-menu, translation-fallback, and RTL language behavior;
- local per-page runtimes and repeated component instances;
- ECharts structured options and callback integration;
- an offline bilingual starter and offline release archive;
- vendor licenses and checksums;
- non-mutating migration rehearsals for SILO, PGSTY, SOW, and Pigsty.
The latest four-site rehearsal built temporary copies successfully. It did not modify or deploy those production repositories.
What remains before release
The public identity and first version must be approved and applied consistently to the module, package, source tags, nested theme tag, archive, and documentation. The target Cloudflare Pages project must then be connected to the source branch, built with the pinned Hugo version, published, and verified at its hosted URL.
Until those gates close, use the preview for evaluation and migration rehearsal, not as an unversioned production dependency.
Read next
2 - OINK implementation diary: from copied shells to one theme
OINK began with an awkward observation: several production documentation sites looked related because they were related, but their common implementation lived as copied files. The visible result was consistent enough; the maintenance model was not.
This diary records how the project moved from repeated site overrides to one directly evolved theme. It focuses on decisions and evidence rather than a commit-by-commit transcript.
Locking the contract
The first useful work was subtraction. We wrote down what the product must be before choosing how to implement it:
- an independent theme derived directly from Docsy;
- one canonical shell, not a switchable skin;
- Hugo Extended as the only consumer build dependency;
- local-first delivery for every theme-owned browser asset;
- multilingual behavior derived from Hugo rather than PGSTY domains;
- reusable components in the theme, business semantics in the site;
- preserved Docsy history, licenses, and upstream traceability.
This ruled out an attractive but costly shortcut: adding params.oink.enabled
and leaving the old shell in place. A mode switch would have made every layout
change, accessibility fix, and test support two products. Direct evolution made
the intended design the only design.
Replacing the shell
The documentation, blog, and API-reference layouts were rebuilt around shared, small partials. The resulting shell includes:
- global navigation and responsive sub-navigation;
- a resizable, foldable sidebar;
- local search and quick links;
- language and color-mode controls;
- breadcrumbs, table of contents, page metadata, and feedback;
- a consistent footer and print layout.
The hard part was not drawing a navbar. It was preserving existing Docsy
extension points while removing copied baseof.html files. Narrow hooks remain;
site-wide shell duplication no longer has to be the normal customization path.
Removing the consumer toolchain
The original dependency chain assumed npm-provided Bootstrap and Font Awesome, and some paths invoked PostCSS. OINK moved the required sources and compiled artifacts into the theme and kept SCSS inside Hugo’s own asset pipeline.
Tests do more than check that hugo succeeds. Fixture traps fail if a consumer
build tries to run Node.js, npm, PostCSS, or Autoprefixer, or if a template uses
resources.GetRemote. LTR and RTL pages go through the same constraint.
This distinction matters: the repository still uses Node for its maintainer test harness. “Hugo-only” describes what a consuming site needs after it has a complete theme, not a ban on development tooling inside the theme repository.
Vendoring browser runtimes
The next layer was every dependency the browser might otherwise fetch: Bootstrap, Font Awesome, fonts, jQuery, Lunr, Mermaid, KaTeX, Markmap, Swagger UI, Redoc, Asciinema, ECharts, Infographic, and their supporting libraries.
Each selected artifact received a source, fixed version, license path, checksum,
and update procedure in theme/VENDOR.json. Licenses live beside the vendored
material. The manifest is validated against the actual files rather than treated
as an aspirational inventory.
PlantUML and Diagrams.net forced a useful distinction. They are service-backed features, not merely JavaScript libraries. OINK refuses to invent a public endpoint: enabling one without a configured service fails the build.
Building the multilingual core
The previous language behavior was scattered across navigation and site-specific
assumptions. The new core starts with Hugo’s configured languages,
.Translations, and .AllTranslations.
The presentation is deliberately stable: one language hides the selector; two or more use the same icon button. Clicking advances by configured weight, while a short hover or keyboard focus opens the complete language menu.
A missing translation falls back to the target-language home page. Language
labels use each language’s own name. The same objects drive lang, direction,
canonical, hreflang, and Open Graph locale metadata, so the visible selector
cannot drift from SEO output.
The tests construct every state with an RTL current language as well as normal LTR cases. Native links and disclosure controls keep keyboard behavior predictable.
Promoting components
Asciinema, ECharts, Infographic, document carousel, details, tabs, cards, and parameter rendering had already proved valuable in PGSTY sites. The work was to turn them from copies into product APIs:
- normalize parameter names and defaults;
- generate unique IDs from page identity and shortcode ordinal;
- load each runtime once per page and omit it from unused pages;
- preserve subpath URLs;
- support repeated identical instances;
- provide print, dark-mode, mobile, keyboard, and reduced-motion behavior;
- retain compatible aliases for imported content.
Product matrices and other business widgets did not move. Reuse is not measured by the number of repositories containing a copy; it requires a stable, business-neutral contract.
Supporting ECharts callbacks
ECharts callbacks are legitimate chart options that JSON and YAML cannot express. Existing pages use them for tooltip formatting, label formatting, and data-dependent colors. Treating those callbacks as a separate migration exception added configuration without creating a sandbox.
The shortcode therefore keeps one direct contract:
- content supplies JSON or YAML, which Hugo parses and safely serializes;
- an optional fenced JavaScript block declares callbacks;
$fn:namevalues reconnect those callbacks after the options are parsed;- authors review executable code under the same trust model as inline HTML and other custom integrations.
Tests cover repeated identical charts, invalid CSS lengths, structured options, and callback registration.
Creating the starter and archive
A contract is easier to trust when the smallest example demonstrates it. The
starter contains bilingual home, docs, blog, and component pages; local search;
dark mode; diagrams; API documentation; and the new components. It has no
package.json and no site workflow.
The offline packager combines theme/, starter/, licenses, the upstream
record, and migration guidance, excluding generated output and dependency
caches. It writes a sidecar SHA-256 file and refuses to overwrite an existing
artifact.
The acceptance test copies the starter and theme to a temporary directory, empties caches, blocks HTTP/HTTPS and Go proxies, builds with Hugo, and examines HTML and CSS for third-party subresources.
Rehearsing four migrations
SILO, PGSTY, SOW, and Pigsty provided the reality check. The rehearsal tool copies each workspace instead of modifying it, removes only classified common overrides, applies a local theme replacement, forbids network and frontend tools, and runs the production build.
The latest rehearsal removed 20 common overrides from SILO, PGSTY, and SOW and 24 from Pigsty. Pigsty retained its three business matrix shortcodes and existing ECharts callbacks. All temporary copies built, producing 1,095, 16, 128, and 2,473 HTML files respectively.
Those numbers prove the rehearsal at the recorded commits. They do not prove that any production repository was changed or any hosted site was deployed.
Turning the sample into OINK docs
The inherited docsy.dev site was valuable as a regression corpus, but it
described only Docsy. The documentation phase did four things:
- made English primary and Simplified Chinese second; a later shell review removed French from the demonstration site;
- translated every core documentation and blog source into a colocated
.zh.mdpage; - preserved English heading IDs explicitly in every Chinese heading;
- added the OINK product guide, announcement, and this implementation diary.
Before translation, we established a terminology and typography guide. A checker then verifies source/translation pairs, heading counts, explicit Chinese IDs, and rendered English/Chinese heading-ID equality.
Historical Docsy release posts remain faithful translations. Their npm-era instructions are historical context, while the OINK architecture and migration guides state the current Hugo-only product contract.
What testing changed
Several tests changed the design rather than merely blessing it:
- subpath fixtures forced every local component URL through Hugo URL handling;
- repeated-instance tests replaced content hashes with page-and-ordinal IDs;
- offline browser checks exposed implicit runtime requests;
- RTL language matrices prevented a selector implementation that only worked for the starter’s two LTR languages;
- ECharts callback fixtures kept callback registration and structured options interoperable;
- migration rehearsals preserved site-specific partials that a blanket
layouts/deletion would have removed.
The strongest test suite is one that constrains the product boundary, not just the current HTML snapshot.
Remaining work
Two release gates remain intentionally open. The public brand, repository, module and package identities, and first version need approval. A real Cloudflare Pages project then needs to build from the source branch and pass hosted verification.
Production migrations should proceed one site at a time, with dedicated branches, preview deployments, visual regression, and rollback artifacts. The temporary four-site rehearsal is a foundation for that work, not a substitute.
Lessons
- Write the product boundary before moving files.
- A local-first promise needs both build-time and browser-time evidence.
- Configuration is healthier when it expresses user choices, not internal implementation branches.
- Translation quality includes stable links, code fidelity, typography, and rendered structure—not prose alone.
- Reuse should remove maintenance copies without absorbing business semantics.
- “Built,” “packaged,” “published,” “deployed,” and “migrated” are different claims and need different evidence.
The result is less dramatic than a rewrite and more useful: one theme that can be understood, built, tested, translated, and migrated as a coherent product.