This is the multi-page printable view of this section. .
Quick start
This path does not start from an empty directory. It starts by cloning the site you are reading, deleting what you do not need, and replacing the rest with your own details. This site is OINK’s regression site: it contains every component and every page type, and it tracks the theme version. Trimming it down is less writing than adding configuration and examples one at a time to an empty directory.
Prerequisites: a machine that can install Hugo Extended and Go, a GitHub account, and ten minutes. No Node.js and no other front-end toolchain.
What you end up with
A bilingual documentation site: your directory tree in the left sidebar, this page’s outline on the right, full-text search and a command palette in the navbar, and light or dark following the system. One Markdown source produces the web page, the print page, plain Markdown and RSS. Hosted on GitHub Pages.

Walkthrough
-
Install Hugo Extended and Go
Besides Git you need two things. Hugo Extended must be
0.160.1or newer: the standard Hugo build has no embedded Sass compiler, cannot compile the theme’s styles, and fails the build. Go resolves modules: OINK is published as a Hugo Module, and Hugo uses Go’s module machinery to download and verifygithub.com/pgsty/oink.macOSLinuxWindowsCheck once afterwards; the output must contain
extended:On other platforms follow the Hugo installation guide and go.dev/dl, taking care to pick the extended build.
-
Clone the documentation site and preview it
Open http://localhost:1313/; the Chinese site is at http://localhost:1313/zh/. The first start downloads the theme module (a few seconds to a minute, depending on the network); after that, edits hot-reload in milliseconds.
The committed
go.modpins the theme version, so the clone builds as it is, with no extra install script.NoteThe
Makefilein the repository is only a set of command aliases.make devandmake checkuse a sibling../oinktheme checkout throughHUGO_MODULE_REPLACEMENTS;make buildandmake servealways use the published version pinned ingo.mod. For a new site,hugo serveris enough. -
Replace the site details
Site identity lives entirely in
hugo.yml.baseURLis a YAML anchor — the real address is written onparams.productionURL, and that is the only place to change it:hugo.ymllanguages.en.titleandlanguages.zh.titleoverride the top-leveltitle, so change both. Every parameter’s meaning and default is in Configuration.Delete the configuration specific to this site. Keeping it points your site at OINK’s repositories and accounts.
Key in hugo.ymlWhat to do services.googleAnalytics.idOINK’s analytics ID. Delete it, or replace it with your own when you want analytics params.commentsgiscus pointing at discussions in pgsty/oink.pgsty.com. Delete the block or point it at your repositoryparams.tdVersionparams.versionparams.version_menuparams.versionsOINK’s version menu. Delete params.github_project_repoA link to the theme repository. Delete languages.<lang>.menus.mainNavbar entries pointing at this site’s sections such as /docs/tutorial. Rewrite for your treeReplace the logo and icons. Replace these three files, keeping the filenames — the theme mounts them by name:
static/static/logo.svgis this site’s own brand lockup and no parameter points at it. Delete it, or replace it with a horizontal wordmark and setparams.wordmark.Replace the content.
content/docs/is OINK’s own theme documentation; delete the whole tree and write your first page:content/docs/_index.mdcontent/blog/can keep one post as a template or be deleted entirely (if you delete it, remove theblogentry frommenus.mainas well). Which directories must stay and which belong to the documentation site itself is in Repository tour.For an English-only site, delete the whole
languages.zhblock and every.zh.mdfile, leaving one language:hugo.ymlTo keep both languages or swap in a different pair, see Languages.
-
Deploy
Create an empty repository on GitHub and replace the local history with your own:
The repository ships with
.github/workflows/pages.yml: a push tomainbuilds and publishes, and it can also be triggered by hand from the Actions page (workflow_dispatch). It pins the Hugo Extended and Go versions, builds with--printPathWarnings --panicOnWarning, and takesbaseURLfrom GitHub Pages, so publishing to a subpath such asexample.github.io/product-docs/needs no configuration change.In the repository, go to Settings → Pages → Build and deployment → Source and choose GitHub Actions. The default is
Deploy from a branch, and leaving it will make the workflow fail at the deploy step.Deletingscripts/means editing the workflowThe
Verify advertised and pinned release matchstep inpages.ymlrunsnode scripts/check-release-pin.mjsto check that the version the site advertises matches the one pinned ingo.mod. Oncescripts/is gone, remove that step andSet up Node.jsfrompages.yml.For Cloudflare Pages, Netlify, Nginx and offline packaging, see Deploy: the build command is always
hugo --gc --minify, and onlybaseURLand the environment variables differ.
Verify
Run a production build locally. It is stricter than the development server, and path warnings fail the build:
It passes when it prints Total in … with no WARN or ERROR. Then check
against the preview:
- The navbar shows your site name and logo, and the browser tab shows your favicon
- The sidebar is your own tree, and every page opens
- Ctrl with K (or ⌘ with K on macOS) opens the command palette and finds the page you just wrote
- In the menu beside the page title, “Edit this page” points at your repository, not
pgsty/oink.pgsty.com - After deploying,
Deploy Oink site to GitHub Pagesis green on the repository’s Actions page
For build errors, see Troubleshooting.
Next steps
- Repository tour — what each cloned directory is, and what can go.
- Writing pages — what a documentation page is made of: front matter, heading anchors, links and images.
- Components — callouts, tabs, field lists, file trees and the rest, one page each.
- Brand and appearance — accent colour, font preset, page width and custom styles.
- Deploy — hosting beyond GitHub Pages, and the acceptance checklist.
Instructions for a coding assistant
The four steps above can be handed to a coding assistant (Claude Code, Codex and the like). Copy the block below and replace the three bracketed items with your own details:
The resulting site is easy for an assistant to read: every page has a .md
plain-text output, the site root has llms.txt, and the menu beside the page
title offers “Copy as Markdown” and “Open in Claude”. See
AI-agent support.
To start from an empty directory instead of this repository, see From scratch and other install methods.
Related
- Repository tour — what each directory is, and the order to delete in
- From scratch and other install methods — starting with
hugo mod init, submodules and offline installation - Local preview — the
hugo serverswitches worth knowing, and previewing drafts - Deploy — configuration per host, and the acceptance checklist
- Troubleshooting — the four common classes of error: build, language, search, platform
1 - Repository tour
This page goes through every file and directory of a pgsty/oink.pgsty.com
clone: what must stay, what to replace with your own details, what belongs to
the documentation site and can be deleted whole — plus a safe order to delete
in.
The theme’s code is not in this repository. It is a Hugo Module pinned by
go.mod and stored in Go’s module cache. This repository holds content,
configuration, and a small number of site-level overrides.
Top-level structure
the cloned my-docs/
- my-docs/
- hugo.ymlthe site's only configuration: identity, languages, menus, params, module imports
- go.modpins the theme version
- go.sumchecksums for the theme module
- content/all content; the directory structure is the sidebar structure
- _index.mdhome page; _index.zh.md is its Chinese counterpart
- search.mdresults page for Google Programmable Search; delete if unused
- docs/documentation tree: OINK's own theme documentation
- blog/blog: engineering notes and release announcements
- assets/resources that go through Hugo processing
- scss/site style overrides, three partials
- images/images that need resizing or cropping
- parts/Markdown and YAML fragments pulled in by the include shortcode
- static/copied to the site root as is, unprocessed
- logo.svgbrand lockup; no parameter points at it
- favicon.svgbrowser tab icon
- favicon.ico
- apple-touch-icon.pngiOS add-to-home-screen
- images/screenshots and diagrams
- layouts/site template overrides: override the narrowest one
- _shortcodes/the site's own shortcodes
- data/data-driven pages
- home/home page sections: en.yaml / zh.yaml
- landing/landing page data
- download/release and download page data
- .github/
- workflows/pages.yml deploys; the other two are this site's regression tests
- tests/documentation site only: Playwright, goldens, build assertions
- browser/Playwright specs
- hugo-build/build assertions
- md-output/Markdown output goldens
- alt-site/alternate-configuration builds
- favicons/
- release-pin/
- fixtures/
- scripts/documentation site only: translation parity and link checks
- check-doc-translations.mjs
- check-markdown-style.mjs
- check-rendered-links.mjs
- check-rendered-markdown.mjs
- check-release-pin.mjs
- Makefilebuild / serve call Hugo directly; dev / check point at a sibling ../oink
- package.jsonthe test toolchain; not used to build the site
- package-lock.json
- playwright.config.mjs
- agent-docs.config.ymlconfiguration for the agent-documentation scoring tool
- AGENTS.mdrepository notes for coding agents
- TRANSLATION.mdthe bilingual translation process
- CONTRIBUTING.md
- README.md
- LICENSEApache-2.0, for the site code
- LICENSE-CC-BY-4.0content licence
- NOTICE
Not listed above: .gitignore, .gitattributes, .nvmrc, .npmrc, and the
generated output excluded by .gitignore — public/ (build output),
resources/ (Hugo’s resource cache) and node_modules/. That last group never
enters version control.
There is no i18n/ in the repository: interface strings (“Previous”, “On this
page” and the like) come from the theme’s 32 language files. To change one of
them, create i18n/zh.yaml at the site root and write only the keys you
override.
What to do with each entry
| Path | What it is | What to do after forking |
|---|---|---|
hugo.yml |
The site’s only configuration file — no config/ directory and no per-environment overrides |
Replace with your details: identity, languages, menus, brand |
go.mod go.sum |
Pin the theme version and record its checksums | Must stay, and both are committed |
content/ |
All content; the directory structure decides the sidebar structure | Must stay; replace the docs/ and blog/ inside it with your own |
content/search.md |
A full-page search results page (layout: search); it only has content when Google Programmable Search is configured (params.gcs_engine_id) |
Delete it when using the theme’s local search |
assets/scss/ |
Site style overrides (_variables_project.scss and friends) |
Keep it to change colours and fonts; empty it if you change neither |
assets/images/ |
Images that need Hugo processing (resize, crop) | Replace with your own |
assets/parts/ |
Fragments the include shortcode pulls in |
Replace or delete with the pages that use them |
static/ |
Copied to the site root as is | Replace with yours: logo, favicon, screenshots |
layouts/_shortcodes/ |
This site’s four shortcodes, none referenced by current content | Can be deleted |
data/home/ |
Home page section data (hero, capability panels) | Change to yours; delete it and the home page falls back to an ordinary page |
data/landing/ data/download/ |
Data for landing pages and the release/download page | Delete if unused |
.github/workflows/pages.yml |
Builds and publishes to GitHub Pages on a push to main |
Keep, adjusted for your repository |
.github/workflows/site-checks.yml browser-quality.yml |
This site’s regression pipelines | Documentation site only; can be deleted |
tests/ scripts/ playwright.config.mjs package.json package-lock.json |
This site’s regression tests and check tooling | Documentation site only; can be deleted |
Makefile |
Shortcuts for developing the theme and the site together (expects a sibling ../oink) |
Documentation site only; can be deleted |
AGENTS.md TRANSLATION.md CONTRIBUTING.md agent-docs.config.yml |
This site’s collaboration conventions | Replace with your own, or delete |
README.md LICENSE LICENSE-CC-BY-4.0 NOTICE |
Description and licences | Replace with your own |
.nvmrc .npmrc |
Node version and npm configuration | Delete along with package.json |
The package.json, tests/ and scripts/ in this repository maintain the
documentation site itself. Building your site is one command:
hugo --gc --minify.
Deletion order
Delete the periphery first, then the content, then the data. Build after each step so a problem points back at one step.
-
Drop the scaffolding
None of this takes part in rendering, and removing it affects no page.
After deleting
scripts/you must edit.github/workflows/pages.yml: remove theSet up Node.jsandVerify advertised and pinned release matchsteps, or the deploy fails there. -
Drop the example content
content/docs/is OINK’s own theme documentation andcontent/blog/its engineering blog; neither has anything to do with your product.Edit
menus.mainunder each language inhugo.ymlat the same time: those entries point at paths such as/docs/tutorialand/blog/releasethat no longer exist.content/_index.mdis the home page — keep it and replace the body with yours. -
Trim the data
The three groups under
data/feed the home page, landing pages and release pages. Keep the home page data and edit it; delete the other two if unused.data/home/en.yamlanddata/home/zh.yamldecide which sections the home page has; each entry is explained in Home and landing pages. Deletingdata/home/entirely still builds, and the home page falls back to an ordinary content page. -
Swap the identity
Finally, change the site name,
params.productionURL,params.github_repoand the brand parameters inhugo.ymlto yours, replace the logo and favicon understatic/, and delete the OINK-specific configuration:services.googleAnalytics,params.commentsand theparams.version*keys. The itemized list is in step 3 of Quick start.
Where the theme lives
The theme is referenced as a Hugo Module, and two places point at it:
hugo.yml declares which theme to use, go.mod pins which version of it, and
go.sum records that version’s checksums. All three files are committed. The
theme source never enters your repository: Hugo downloads it into Go’s module
cache, and hugo mod graph shows what actually resolved.
Upgrade to the newest version:
Pin to one version:
Both commands rewrite go.mod and go.sum. A production site pins a release
tag rather than following main. What to check before and after an upgrade, and
how to roll back, is in Upgrade.
Site overrides
Files under layouts/ shadow the theme’s files of the same name, following
Hugo’s template lookup order. This site has only one kind:
layouts/_shortcodes/*.html— the site’s own shortcodes. Product documentation that needs a shortcode with business meaning puts it here too.
Self-linking heading anchors come from the theme’s own
_markup/render-heading.html; a site does not need to build that hook.
To change the shell (sidebar, footer, page end), override the narrowest partial
rather than copying baseof.html wholesale — a copy has to be merged by hand at
every theme upgrade.
Verify
Build after each deletion step so an error points at what you just removed:
Once the deleting is done, all of this should hold:
- The build ends with
Total in …and noWARNorERROR - No navbar entry links to a deleted directory
- Headings still have their self-link anchors (the theme’s own heading render hook; the site needs no override)
git statusshows nopublic/orresources/
Related
- Quick start — the full clone, configure and deploy path
- From scratch and other install methods — building from an empty directory instead of trimming
- Organizing content — how the
content/tree becomes the sidebar - Configuration — every
hugo.ymlkey and its default - Upgrade — upgrading the theme module, and the migration toolkit
2 - From scratch and other install methods
This page builds a minimal OINK site in an empty directory: a dozen lines of
hugo.yml plus one hugo mod get gives a single-language site you can preview.
The cost is that the home page, the example content and any component usage to
copy from are all yours to write.
An existing Hugo site needs no scaffolding: install the theme module, add the
three Goldmark prerequisites (see Writing hugo.yml), and leave the
content alone. For an existing Docsy site, see Upgrade.
The second half weighs four install methods: Hugo Module, Git submodule, offline archive, pinned clone.
From an empty directory to the first page
-
Create the skeleton and fetch the theme
What follows
hugo mod initis your own site’s module path, usually the repository address.hugo mod getwritesgo.modandgo.sum, and both are committed.The newest version number is on GitHub Releases; the
v0.6.0on this page is what this site currently pins. A production site pins a release tag rather than followingmain:@latestis a one-off resolution, not a version policy. -
Writing
hugo.ymlRename the
hugo.yamlthathugo new sitegenerated tohugo.yml(Hugo accepts both; this documentation uses the latter throughout) and replace its contents with the following, which builds as it stands:hugo.ymlWhat each of the five blocks governs:
Block Governs Consequence of omitting it Top level + languagesSite name, domain, languages and navbar menu A wrong baseURLsends every absolute link astray in productionmarkup.goldmarkThe three component prerequisites An attribute line becomes a literal {.steps}in the proseparamsSearch, repository links, shell switches Interactive features stay off; the theme does not decide for the site outputsThe per-page .md,llms.txtand print pagesNo “Copy as Markdown” in the page menu, and no print view moduleReferences the theme and declares the Hugo floor The build cannot find the theme Mathematics additionally needs Goldmark’s passthrough extension; see Math. Every key’s full meaning and default is in Configuration.
-
Write the first page
Every top-level directory under
content/is a section, and the directory structure is the sidebar structure. A documentation section needs at least an_index.md:content/docs/_index.mdcontent/docs/install.mdWrite explicit
{#id}anchors on headings: when a translation is added later, the two languages’ anchors have to correspond. How to write a page is in Writing pages. -
Preview
Open http://localhost:1313/ and the sidebar shows Docs → Install. Edits hot-reload in milliseconds.
Other install methods
The steps above use a Hugo Module. The other three address particular
constraints: network isolation, a platform that requires the build input to
contain the whole theme tree, or an organization that reviews its own copy of
the theme. Apart from hugo mod vendor, none of them creates a Go module, and
the site references the theme with theme: oink rather than module.imports.
The shared cost is that version resolution and integrity checking become your
responsibility.
Hugo Module (recommended)
The only method where Hugo resolves the version itself, verifies the checksum,
and leaves an audit record in go.sum. hugo mod graph shows what actually
resolved and hugo mod get -u upgrades. It needs Go on the machine.
Git submodule
Record an exact theme commit in the site repository:
CI must initialize the submodule before running Hugo, or themes/oink is an
empty directory:
Offline archive
For network-isolated environments. Two paths, both prepared on a connected machine and carried in whole.
With hugo mod vendor, the resolved theme source is frozen into the site
directory, and later builds need neither the network nor Go.
When _vendor/ exists Hugo prefers it (hugo mod graph prints +vendor), and
module.imports in hugo.yml stays as it is. This step needs Go; the builds
after it do not. Upgrading the theme means returning to a connected environment
and running hugo mod get and hugo mod vendor again.
_vendor/ collects only the directories the theme mounts (assets, data,
i18n, layouts, static) plus hugo.yaml and theme.toml. It does not
include LICENSE, NOTICE or VENDOR.json. To redistribute that archive, take
those three files from the theme repository as well.
With a tag source archive, no Go module is created; a version of the theme is
simply unpacked into themes/oink/.
The theme repository’s root is the module root, so unpacking lands directly on
layouts/, assets/, i18n/ and static/ with no further level to descend
into. Redistribution must keep LICENSE, NOTICE and VENDOR.json; the last
records each third-party runtime’s version, source, licence path and SHA-256,
and is what an offline audit rests on.
When moving between machines, generate the archive and its checksum from an immutable tag on the connected side:
Carry the archive and its .sha256 into the isolated environment, verify, then
unpack:
An archive produced this way is your own artifact, not a project release. Whether a given tag’s release page carries an archive and a checksum file varies by release; verify the checksum independently when using a public attachment.
Before building offline, confirm the archive is complete. All eleven of these must be present:
themes/oink/
- oink/
- go.modmodule path declaration, used when resolving as a Hugo Module
- hugo.yamltheme default parameters and the Hugo version floor
- theme.tomltheme metadata, required by the theme: oink method
- LICENSEApache-2.0
- NOTICEupstream attribution; must be kept on redistribution
- VENDOR.jsonthird-party runtime manifest: version, source, licence path, SHA-256
- assets/SCSS, JS and the third-party runtimes shipped with the theme
- layouts/templates, partials, shortcodes, render hooks
- static/font files, published as is
- i18n/32 interface language files
- data/the SPDX licence table behind the page-end attribution line
Pinned clone
For a hosting platform that requires the build input to contain the whole theme tree:
The difference from a submodule is that the theme files enter your repository
history directly, without the .gitmodules indirection. Record the commit that
was finally resolved and the procedure for restoring it.
The four methods compared
| Method | Needs Go | Version auditable | Theme source in your repository | Use when |
|---|---|---|---|---|
| Hugo Module | Yes | go.sum verifies automatically |
No | The default |
| Git submodule | No | The repository records the commit | By reference | The theme source has to be in the repository |
| Offline archive | No | Checksums verified by hand | Yes | Network isolation |
| Pinned clone | No | You record it yourself | Yes | The platform requires a complete tree |
Bootstrap, Font Awesome, the fonts, and the search and diagram runtimes all
ship with the theme. A site needs no node_modules, no PostCSS, no RTLCSS and
no CDN. Tutorials that install npm dependencies for a Docsy site describe
upstream Docsy’s process and do not apply to OINK.
Developing against a local theme checkout
This section applies only when changing the theme and the site together. Clone the two repositories as siblings:
Use the HUGO_MODULE_REPLACEMENTS environment variable to substitute the local
checkout temporarily, leaving go.mod untouched:
The documentation site’s Makefile is an alias for exactly these commands, and
make dev and make check expect the theme checkout at the sibling ../oink:
A Go workspace (go work init plus HUGO_MODULE_WORKSPACE=go.work) is an
equivalent alternative. Both apply to the local machine only: CI and production
builds use the version in go.mod, and go.work is never committed.
Verify
It passes when the build ends with Total in … and no WARN or ERROR. Then
confirm:
/docs/opens and the sidebar holds the page you wrote- The navbar has a search box that finds the heading you just wrote
- The light/dark toggle is present, and code block colours follow it (which shows
markup.highlight.noClasses: falsetook effect) git statusshowsgo.modandgo.sum, and nopublic/orresources/
Related
- Quick start — the other path: clone the documentation site and trim it
- Repository tour — what each directory of the documentation site is
- Configuration — every
hugo.ymlkey and its default - Writing pages — how to keep writing after the first page
- Upgrade — upgrading the theme module, and migrating from Docsy