# Oink 0.3.0 — Authoring, navigation, and a lighter page

> Oink 0.3.0 adds enhanced code blocks and code groups, everyday content primitives, nested navigation with a Command Palette, semantic typography presets, and removes jQuery from every page.

---

LLMS index: [llms.txt](/llms.txt)

---

**Release date:** 2026-08-12 · **Theme tag:**
[v0.3.0](https://github.com/pgsty/oink/tree/v0.3.0) · **Repository:**
[pgsty/oink](https://github.com/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](#breaking-changes).

## Release highlights {#release-highlights}

### Code blocks and code groups {#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](/docs/components/code-blocks/) for the full attribute
contract.

### Everyday content primitives {#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](/docs/components/) for each contract.

### Navigation and Command Palette {#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](https://github.com/pgsty/oink/blob/v0.3.0/docs/prd4-migration-guide.md)
for the complete configuration surface.

### Typography presets {#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](https://github.com/pgsty/oink/blob/v0.3.0/docs/typography-tokens.md).

### A lighter page {#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 {#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 {#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:

```html
<!-- layouts/_partials/hooks/head-end.html -->
<script src="{{ (resources.Get "js/jquery.min.js").RelPermalink }}"></script>
```

**`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:

```scss
// Before
body {
  font-family: 'My Sans', sans-serif;
}

// Oink 0.3.0
:root {
  --td-body-font-family: 'My Sans', sans-serif;
}
```

## Upgrade to 0.3.0 {#upgrade}

1. Check whether any project JavaScript depends on the global `$`, and bundle
   jQuery yourself if so.
2. Remove any direct reference to `static/js/tabpane-persist.js`; authored
   `tabpane` content itself does not change.
3. Move raw `body` or heading font overrides to the typography roles.
4. Decide whether to opt into assistant handoff links. If enabled, review URLs
   for sensitive query or fragment data and disclose the third-party boundary.
5. Update the Hugo Module and tidy the module graph.
6. Build and inspect representative documentation, blog, mobile, print, and
   color-mode pages.

```sh
hugo mod get github.com/pgsty/oink@v0.3.0
hugo mod tidy
hugo --gc --minify
```

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 {#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 {#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 {#full-change-set}

See the complete source diff from
[v0.2.1 to v0.3.0](https://github.com/pgsty/oink/compare/v0.2.1...v0.3.0).
