Oink 0.3.0 — Authoring, navigation, and a lighter page
Release date: 2026-08-12 · Theme tag: v0.3.0 · Repository: pgsty/oink
Publication gate: the linked tag must resolve, the project site must pin that exact tag, and the hosted checks must pass. Until then, treat this source page as release-candidate material.
Oink 0.3.0 is the authoring and navigation release. Writing a page gets a modern code-block presentation and a set of small, everyday components; reading one gets nested navigation and a Command Palette; and every page gets measurably lighter because jQuery is gone.
The module path, the minimum Hugo version, and the Hugo-only consumer build are unchanged. Three changes can affect an existing site and are documented in Breaking changes.
Release highlights
Code blocks and code groups
Ordinary fenced code blocks now render a real code surface: an optional
filename, a language label, a copy button rendered by the server rather than
injected by script, opt-in wrapping, and collapsing for long listings. Hugo’s
own highlighting options — line numbers, line anchors, hl_lines, tab width —
keep working exactly as before.
Copy behaviour is deterministic rather than guessed. Session lexers such as
console and shell-session default to copying commands without prompts and
output; every other language copies the whole block. copy=command is rejected
on lexers that cannot distinguish the two, because silently copying the wrong
thing is worse than a build error.
The code-group shortcode groups alternatives — package managers, languages,
platforms — into synchronized tabs with a stable URL hash, so a link can open
the exact variant a reader needs. Legacy tabpane content keeps working and
keeps its storage key.
See Code blocks for the full attribute contract.
Everyday content primitives
Alongside the existing large components, 0.3.0 adds the small ones authors
actually reach for daily: badge, kbd, fields, filetree, gallery, and
an opt-in image_zoom. All of them render semantic HTML, none of the
non-interactive ones load JavaScript, and each has a defined presentation in
print and Markdown output as well as HTML.
A standalone public icon shortcode is deliberately still deferred; components
use a private, allowlisted registry for their own decoration until that API is
designed properly.
See Components for each contract.
Navigation and Command Palette
Top-level menus support one level of dropdowns on desktop and matching accordions on mobile, with the parent link and the disclosure control operated independently so a parent stays navigable. Flat menus are untouched.
Local search becomes a Command Palette with three modes: an empty query offering
quick links and page actions, a text query returning grouped page results, and a
> prefix that searches commands only. Pages can contribute search_keywords,
a positive search_boost, and canonical exclusion; ranking applies the same
boost on both the Lunr and CJK substring paths.
Page actions and palette commands now run through one shared registry, so Copy
text, Open in ChatGPT, Open in Claude, View source, View edit history, printing,
and switching theme, language, or version behave identically wherever they are
invoked. Assistant prompts resolve the browser URL at activation time,
preserving the deployed host, query string, and fragment; history links derive
from the same repository path as Edit this page. Assistant handoff links are
disabled by default; sites must opt in with
params.ui.page_context_menu.assistant_links: true. On activation, the full URL
leaves the site, so do not place secrets in its query or fragment.
Press / outside an editable control to open the Palette directly in command
mode. Cmd/Ctrl-K remains the general entry point, and the single-character
shortcut yields to inputs, textareas, selects, and contenteditable regions.
Sidebars gain an icon-density policy — all, groups, or none. The
compatibility default stays all; the starter example opts into groups.
See the migration reference for the complete configuration surface.
Typography presets
Font choices move behind seven semantic --td-*-font-family roles covering UI,
body, headings, code, display text, metadata, and print. Two validated presets
ship: technical, which preserves the current Oink appearance, and system,
which uses the platform stack and requests no Oink brand fonts at all. Existing
Docsy and Bootstrap Sass font variables seed the roles, so prior overrides keep
working.
This is the typography slice of a larger design-token effort. Colour, surface, radius, density, and appearance presets are not part of this release.
See Typography tokens.
A lighter page
jQuery is gone. It was previously fetched render-blocking in <head> on every
page — 87.5 KB before any content — while the theme’s own architecture loads
feature runtimes only on pages that use them. Nothing in the shell needed it,
and the superseded offline-search.js runtime it powered was already replaced
by the Command Palette.
Two other costs were removed rather than accepted. The active output format is now read from the page store instead of being re-derived thousands of times per build, and the shell configuration is cached per language; on a 576-page build that is 357ms of template time reduced to 72ms, with byte-identical output. CJK search folds its index fields once instead of re-lowercasing the entire corpus on every keystroke, taking an 800-document query from 3.44ms to 0.34ms per character typed.
On the measured project-site snapshot, removing jQuery and the superseded search runtime saved about 88 KB from a typical documentation page’s combined CSS and JavaScript. Exact totals vary as later candidate assets change.
Correctness and localization
This release also closes several less visible correctness gaps. Markdown pages
link to llms.txt only when the active language actually publishes one, and the
index no longer treats off-site menu chrome as content. Internal configured
commands stay under a subpath deployment, while shared content types resolve to
the right product root. The archived-version banner and Giscus fallback are now
localized, and print or Markdown output strips interaction-only Image Zoom
attributes regardless of quoting style. Legacy search links also percent-encode
their query text instead of truncating a query at &.
Browser runtime tests now run in theme CI instead of relying on Hugo bundling as their only signal. Terminal recordings also wait for their configured font before fitting the player, avoiding geometry based on a fallback font.
Breaking changes
jQuery is no longer loaded. The third-party inventory previously listed it
as part of the UI foundation, so a consuming site’s own scripts may rely on the
global $. No theme feature requires it. Sites that need it must now bundle it
through project JavaScript:
static/js/tabpane-persist.js is removed. assets/js/code-tabs.js took
over the legacy persistence contract, keeping the td-tp-persist storage key
and data attribute, so authored tab content is unaffected. Only a site that
referenced the published file path directly needs to drop that reference.
Body and heading typography roles apply directly to content. A site that
previously restyled raw body or heading selectors should move to the matching
--td-*-font-family role or the established Sass variable:
Upgrade to 0.3.0
- Check whether any project JavaScript depends on the global
$, and bundle jQuery yourself if so. - Remove any direct reference to
static/js/tabpane-persist.js; authoredtabpanecontent itself does not change. - Move raw
bodyor heading font overrides to the typography roles. - Decide whether to opt into assistant handoff links. If enabled, review URLs for sensitive query or fragment data and disclose the third-party boundary.
- Update the Hugo Module and tidy the module graph.
- Build and inspect representative documentation, blog, mobile, print, and color-mode pages.
No Markdown content rewrite is required. Existing fenced code blocks, tabpane
content, flat menus, shortcodes, and ordinary Docsy-compatible pages continue to
work unchanged.
Compatibility
| Contract | Oink 0.3.0 |
|---|---|
| Hugo | Extended 0.160.1 or newer; unchanged |
| Module path | github.com/pgsty/oink; unchanged |
| Consumer frontend toolchain | None; unchanged |
| Required content migration | None |
| Required configuration migration | None; assistant links are opt-in |
| Required project-JS migration | Only if it depends on global jQuery |
Verification
The 0.3.0 candidate is exercised through the sibling Oink project site, so the site builds against the candidate theme rather than its last pinned release. Before publication, the theme gate must pass the complete contract suite, a warning-free example-site build on the minimum and current Hugo versions, both typography presets, and the browser runtime unit tests. The site gate must pass formatting, bilingual page pairs and stable heading IDs, rendered Markdown and internal links, Hugo Module fixtures, alternate-configuration builds, Markdown and favicon goldens, responsive and component browser behaviour, and axe accessibility checks. The tag, public-module resolution, site version pin, and hosted smoke tests remain separate post-approval gates.
Full change set
See the complete source diff from v0.2.1 to v0.3.0.