Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9ea2192
fix(markmap): follow article text color in screen media
imbajin Oct 12, 2026
4e40bd8
fix: keep fixed TOC rail above the visible footer
imbajin Oct 12, 2026
db7a5cb
fix: preserve readable widths in technical tables
imbajin Oct 12, 2026
5c299ad
test(markmap): guard screen-only text token contract
imbajin Oct 13, 2026
16f5a2a
fix(tables): fit ordinary prose and isolate explicit wide tables
imbajin Oct 13, 2026
acad022
test(tables): guard fitting wide-mode and nested static boundaries
imbajin Oct 13, 2026
7121e6a
test(toc): cover initial restoration observer fallback and mixed events
imbajin Oct 13, 2026
34fab16
test(tables): add fitting prose and explicit technical-width inputs
imbajin Oct 13, 2026
714ff12
test(tables): mirror native layout-policy inputs in Chinese
imbajin Oct 13, 2026
d20f908
fix(tables): preserve consumer overrides with zero-weight guards
imbajin Oct 13, 2026
8773bd1
test(tables): tighten width guard and verify technical columns
imbajin Oct 14, 2026
de697e6
Merge upstream main into fix/markmap-theme-text
imbajin Oct 15, 2026
8ee2e78
Merge upstream main into fix/toc-footer-offset
imbajin Oct 15, 2026
4524378
Merge upstream main into fix/readable-technical-tables
imbajin Oct 15, 2026
548e36d
fix(toc): refresh footer offset after tab changes
imbajin Oct 16, 2026
56da1aa
fix(toc): use layout viewport for footer clearance
imbajin Oct 17, 2026
8372a67
build: advance Hugo to 0.168 with standalone Dart Sass
Vonng Oct 17, 2026
206830d
Merge branch 'codex/hugo-168-sass' into codex/fix-footer
Vonng Oct 17, 2026
c42fe62
fix: refresh footer clearance after content reflow
Vonng Oct 17, 2026
adf04f0
Merge branch 'codex/hugo-168-sass' into codex/fix-tables
Vonng Oct 17, 2026
75412bc
Merge remote-tracking branch 'review/pr48' into codex/readability-int…
Vonng Oct 17, 2026
7f2c03b
fix: scope technical table layout to explicit opt-in
Vonng Oct 17, 2026
f82e761
Merge branch 'codex/fix-footer' into codex/readability-integration
Vonng Oct 17, 2026
729326d
Merge branch 'codex/fix-tables' into codex/readability-integration
Vonng Oct 17, 2026
ea1d80a
docs: record the completed readability repairs
Vonng Oct 17, 2026
bf4596e
test: keep technical table fixture anchors unique
Vonng Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

env:
GO_VERSION: 1.27.0
HUGO_VERSION: 0.166.0
HUGO_VERSION: 0.168.0
GOTOOLCHAIN: local

jobs:
Expand Down Expand Up @@ -51,6 +51,17 @@ jobs:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true

- name: Install Dart Sass 1.105.2
shell: bash
run: |
sass_archive="$RUNNER_TEMP/dart-sass-1.105.2-linux-x64.tar.gz"
curl --fail --location --silent --show-error \
--output "$sass_archive" \
https://github.com/sass/dart-sass/releases/download/1.105.2/dart-sass-1.105.2-linux-x64.tar.gz
echo "a530ee08d5792c80bdb9d9614b45fb58219c890cb42a280a80775f0af9c29582 $sass_archive" | sha256sum --check
tar --extract --gzip --file "$sass_archive" --directory "$RUNNER_TEMP"
echo "$RUNNER_TEMP/dart-sass" >> "$GITHUB_PATH"

- name: Install PDF inspection tools
run: |
sudo apt-get update
Expand Down Expand Up @@ -192,6 +203,17 @@ jobs:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true

- name: Install Dart Sass 1.105.2
shell: bash
run: |
sass_archive="$RUNNER_TEMP/dart-sass-1.105.2-linux-x64.tar.gz"
curl --fail --location --silent --show-error \
--output "$sass_archive" \
https://github.com/sass/dart-sass/releases/download/1.105.2/dart-sass-1.105.2-linux-x64.tar.gz
echo "a530ee08d5792c80bdb9d9614b45fb58219c890cb42a280a80775f0af9c29582 $sass_archive" | sha256sum --check
tar --extract --gzip --file "$sass_archive" --directory "$RUNNER_TEMP"
echo "$RUNNER_TEMP/dart-sass" >> "$GITHUB_PATH"

- name: Check translation key parity
run: python3 bin/check-i18n.py

Expand Down
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ make -C ../oink.pgsty.com dev

## Theme contracts

- As of 2026-10-15, Hugo Extended 0.166.0 is the single development, CI, and
- As of 2026-10-17, Hugo Extended 0.168.0 is the single development, CI, and
release-test baseline and the current minimum. Advance it with stable Hugo
releases; update CI pins, module/theme requirements, checkers, and current
bilingual documentation together. Do not run older-Hugo compatibility
matrices, adapt code for old versions, or add compatibility shims unless the
user explicitly requests it. Historical release/research records are not
current support requirements.
- SCSS builds use standalone Dart Sass 1.105.2 on `PATH`; CI pins the same
distribution. Preserve the project-variable import hooks and strict warnings.
- The module declares Go 1.27.0; CI pins Go 1.27.0. Record the actual local
tool versions when reporting validation.
- Run checkers with `--panicOnWarning` when they build Hugo output. Ordinary
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ All notable changes to OINK are documented here. The project follows

## [Unreleased]

### Changed

- Advance the minimum and sole development, CI, and release-test baseline to
Hugo Extended 0.168.0.
- Refresh homepage and RSS output goldens for Hugo's generator-tag placement
and automatic summaries that extend to preserve balanced HTML containers.

- Compile main and ReDoc styles with standalone Dart Sass 1.105.2, preserving
project-variable hooks and the separate Font Awesome stylesheet. Install
Dart Sass on `PATH` before building. The four known Bootstrap/import-scope
language deprecations are scoped out; other warnings remain publication-fatal.
- Load the dark Chroma palette with Sass modules and correct the adjacent
download-channel selector for Dart Sass.

### Fixed

- Keep Markmap node text readable across light/dark modes and visual presets,
while retaining dark text for print.
- Keep the fixed right rail above the footer, including pages without a
sidebar and content reflows from tabs, blog view changes, and loaded media.
- Add `.td-table--wide` for technical tables that need intact identifiers and
bounded descriptions inside a horizontal scroll region. Ordinary tables,
field lists, and static outputs retain their existing layout.

## [1.3.0] — 2026-10-15

Resource-backed Expo albums with shared navigation, an original-image viewer,
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a href="https://oink.pgsty.com/"><img alt="Website" src="https://img.shields.io/badge/website-oink.pgsty.com-17385c?style=flat-square"></a>
<a href="https://github.com/pgsty/oink/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/pgsty/oink?display_name=tag&amp;sort=semver&amp;style=flat-square"></a>
<a href="https://github.com/pgsty/oink/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/pgsty/oink/ci.yml?branch=main&amp;label=CI&amp;style=flat-square"></a>
<a href="https://gohugo.io/"><img alt="Hugo Extended 0.166.0 or newer" src="https://img.shields.io/badge/Hugo_Extended-%E2%89%A50.166.0-ff4088?logo=hugo&amp;logoColor=white&amp;style=flat-square"></a>
<a href="https://gohugo.io/"><img alt="Hugo Extended 0.168.0 or newer" src="https://img.shields.io/badge/Hugo_Extended-%E2%89%A50.168.0-ff4088?logo=hugo&amp;logoColor=white&amp;style=flat-square"></a>
<a href="https://github.com/pgsty/oink/blob/main/LICENSE"><img alt="Apache 2.0" src="https://img.shields.io/github/license/pgsty/oink?label=license&amp;style=flat-square"></a>
</p>

Expand Down Expand Up @@ -41,7 +41,7 @@ print and RSS for publication, semantic Markdown for reuse, and optional
## Why OINK

- **One deterministic toolchain.** Resolve the Hugo Module once, then build and
preview with one `hugo` binary.
preview with Hugo and the standalone Dart Sass compiler.
- **Complete publishing surfaces.** Docs, Blog, Book, Expo image albums, OpenAPI, releases,
downloads, and data-driven landing pages share one navigation and visual
system.
Expand All @@ -63,8 +63,8 @@ print and RSS for publication, semantic Markdown for reuse, and optional
machine-readable outputs are tested as separate contracts.

OINK 1.3.0 adds the Expo image-album shell and improves navigation, Landing,
and aggregate Print output. It uses Hugo Extended 0.166.0 as its minimum and
single development, CI, and release-test baseline. See the
and aggregate Print output. Unreleased development now requires Hugo Extended
0.168.0 and Dart Sass 1.105.2; v1.3.0 retains its original toolchain. See the
[changelog](CHANGELOG.md) for the complete release notes.

## Start a site
Expand All @@ -82,7 +82,8 @@ repository's internal fixtures.
· [Setup guide](https://oink.pgsty.com/docs/start/)

For development and validation, use Git, Go 1.27 or newer, and Hugo Extended
0.166.0. The site does not require Node.js. To evaluate the neutral template locally:
0.168.0 with [Dart Sass 1.105.2](https://github.com/sass/dart-sass/releases/tag/1.105.2)
on `PATH` (the standalone distribution, not the npm package). The site does not require Node.js. To evaluate the neutral template locally:

```sh
git clone https://github.com/pgsty/oink-starter.git
Expand Down Expand Up @@ -211,7 +212,8 @@ assuming drop-in compatibility.

| Dependency | Policy |
| --- | --- |
| Hugo | **Extended 0.166.0 or newer**; development, CI, and release tests use 0.166.0 |
| Hugo | **Extended 0.168.0 or newer**; development, CI, and release tests use 0.168.0 |
| Dart Sass | Standalone 1.105.2 on `PATH`; no Node.js or npm dependency |
| Go | 1.27 or newer for Hugo Module resolution; not needed when using an offline archive or submodule |
| Node.js | Not required to build or run an OINK site |
| Locales | Native OINK interface text for all 31 locale names shipped by Docsy, plus generic `zh`; every bundle owns the same 218-message schema, including CLDR plural forms, with no English placeholder blocks |
Expand Down
23 changes: 15 additions & 8 deletions assets/js/docs-shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,21 @@

/* --------------------------------------------------------- footerOffset */

// Shorten the fixed sidebar as the footer enters the viewport.
// Shorten fixed rails as the footer enters the viewport.
function initFooterOffset() {
var footer = document.querySelector('[data-td-shell-footer]');
var panel = document.querySelector('.td-shell-sidebar__panel');
var panel = document.querySelector(
'.td-shell-sidebar__panel, .td-shell-toc__panel',
);
if (!footer || !panel) return;
var frame = 0;

function update() {
frame = 0;
var viewportHeight = window.visualViewport
? window.visualViewport.height
: window.innerHeight;
// Fixed rails and footer bounds both use layout-viewport coordinates.
var offset = Math.max(
0,
viewportHeight - footer.getBoundingClientRect().top,
window.innerHeight - footer.getBoundingClientRect().top,
);
if (offset > 0) offset += 1;
html.style.setProperty('--td-shell-footer-offset', offset + 'px');
Expand All @@ -159,14 +159,21 @@

window.addEventListener('scroll', schedule, { passive: true });
window.addEventListener('resize', schedule);
document.addEventListener('td:tab-shown', schedule);
if (window.visualViewport) {
window.visualViewport.addEventListener('scroll', schedule, {
passive: true,
});
window.visualViewport.addEventListener('resize', schedule);
}
if ('ResizeObserver' in window)
new ResizeObserver(schedule).observe(footer);
if ('ResizeObserver' in window) {
var observer = new ResizeObserver(schedule);
observer.observe(footer);
// Tabs, blog views and loaded media move the footer without resizing it.
// Observe normal-flow content; the fixed rails never size this element.
var main = document.getElementById('td-main-content');
if (main) observer.observe(main);
}
update();
}

Expand Down
4 changes: 3 additions & 1 deletion assets/scss/td/_chroma.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use "sass:meta";

// Class-based syntax highlighting (Chroma).
//
// Requires `markup.highlight.noClasses: false`, which the theme sets in its
Expand Down Expand Up @@ -144,7 +146,7 @@

@if $enable-dark-mode {
[data-bs-theme='dark'] {
@import 'chroma/dark';
@include meta.load-css('chroma/dark');

.chroma {
--td-code-hl-bg: color-mix(
Expand Down
10 changes: 10 additions & 0 deletions assets/scss/td/_content-components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,16 @@
--term-color-7: #5d7189;
}

// Markmap declares its text token on both the wrapper and the generated SVG.
// Override both inside article content, with enough specificity to survive
// the vendor stylesheet injected after ours. The body token follows live
// mode/preset changes; keep the vendor's dark ink when printing on white.
@media screen {
.td-content .markmap {
--markmap-text-color: var(--bs-body-color);
}
}

// Markmap highlights fenced code inside its nodes with highlight.js, whose
// vendored `default.min.css` is a light-only theme with no dark counterpart --
// unlike Swagger UI and DocSearch, which carry their own and only need the
Expand Down
32 changes: 32 additions & 0 deletions assets/scss/td/_content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,36 @@
margin-bottom: 0;
}

@media screen {
// Structural guards must not raise the cost of consumer overrides.
&:where(:not(.td-table-scroll--static)) > table:where(.td-table--wide) {
// Preserve ordinary tables. Authors opt technical identifiers into a
// wider measure; the wrapper still contains any horizontal overflow.
width: max-content;
max-width: none;

> :where(thead, tbody, tfoot) > :where(tr) > :is(th, td) {
// Unlike anywhere, break-word keeps technical tokens in the
// intrinsic minimum width, with an escape hatch for very long ones.
overflow-wrap: break-word;
word-break: normal;

a,
code {
overflow-wrap: inherit;
word-break: inherit;
}
}

> :where(thead, tbody, tfoot) > :where(tr) > td {
// Bound prose's preferred measure instead of making a long
// description one enormous line. Auto layout may distribute extra
// space; matrix headers keep their explicit nowrap behavior.
max-width: 40ch;
}
}
}

&--full {
width: 100%;
max-width: none;
Expand All @@ -205,6 +235,8 @@
&--static:not([tabindex]) {
max-width: none;
overflow: visible;
// A nested static viewport must not inherit an outer screen cell's rule.
overflow-wrap: anywhere;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion assets/scss/td/_release.scss
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
padding-block: 0.5rem 2rem;
scroll-margin-block-start: calc(var(--td-scroll-padding-top, 0px) + 1rem);

+ & {
& + & {
padding-block-start: 2rem;
border-block-start: 1px solid var(--bs-border-color);
}
Expand Down
3 changes: 2 additions & 1 deletion assets/scss/td/shell/_toc.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
@include media-breakpoint-up(xl) {
position: fixed;
inset-block-start: var(--td-shell-nav-h);
inset-block-end: 0;
// Share the sidebar's available height when the footer enters view.
inset-block-end: var(--td-shell-footer-offset, 0px);
inset-inline-end: 0;
width: var(--td-shell-toc-w);
z-index: 20;
Expand Down
12 changes: 12 additions & 0 deletions bin/check-content-primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def check_outputs(public: Path) -> list[str]:
require(all(item.path.is_file() for item in chunks), "lists runtime chunk is missing", errors)
lists_bundle = combined_source(public, lists)

# The explicit width marker survives HTML rendering; Markdown preserves
# authored GFM and its attribute lines. Actual cell sizes and scroll behavior are tested in the site.
for prefix in ("", "zh/"):
technical = (public / f"{prefix}fixtures/table-layout-policy/index.html").read_text()
technical_md = (public / f"{prefix}fixtures/table-layout-policy/index.md").read_text()
require('id="wide-technical-table"' in technical and 'class="td-table--wide"' in technical,
f"{prefix} technical table lost its explicit width marker", errors)
require('class="matrix full-width td-table--wide td-table--matrix"' in technical,
f"{prefix} wide marker displaced existing table modifiers", errors)
require("HG_PD_GRPC_HOST" in technical_md and "{#wide-technical-table .td-table--wide}" in technical_md and "<table" not in technical_md,
f"{prefix} technical table Markdown lost authored content or acquired HTML", errors)

# --- Badge / Kbd / Fields shortcode / full-width table -------------------
for marker in (
'class="td-badge td-badge--warning">Beta</span>',
Expand Down
9 changes: 6 additions & 3 deletions bin/check-namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,15 @@
}

# Custom properties a third-party runtime reads from its own stylesheet: the
# theme sets them to theme its player, so they cannot carry a --td- prefix.
# theme sets them to theme its integrations, so they cannot carry a --td- prefix.
# asciinema-player.css reads --term-* (palette, font, metrics); renaming them
# leaves the player on its built-in black-on-white defaults inside an OINK
# terminal surface, which is how the 0.5 sweep broke it.
# terminal surface, which is how the 0.5 sweep broke it. Markmap reads its
# --markmap-text-color token on the SVG; prefixing it leaves dark text on the
# dark article canvas.
THIRD_PARTY_PROPERTY = re.compile(
r"^--term-(color-(foreground|background|\d+)|font-family|line-height|cols|rows)$"
r"^--(?:term-(color-(foreground|background|\d+)|font-family|line-height|cols|rows)"
r"|markmap-text-color)$"
)
BEM_CLASS = re.compile(r"^[a-z][a-z0-9-]*(?:__|--)[a-z0-9_-]+$")
PLAYER_THEME_RE = re.compile(
Expand Down
6 changes: 6 additions & 0 deletions bin/check-shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ def check_sources() -> list[str]:
"collapsed rail controls no longer align with the article topline", errors)
require(not re.search(r"\b(?:width|flex-basis)\s+var\(--td-motion-", toc_styles),
"responsive TOC widths must not animate and make the article rebound during resizing", errors)
fixed_toc = scss_block(toc_styles, "\n.td-shell-toc__panel {")
flow_toc = scss_block(toc_styles, " .td-shell-layout--toc-flow .td-shell-toc__panel {")
require("inset-block-end: var(--td-shell-footer-offset, 0px)" in fixed_toc
and "position: sticky" in flow_toc
and "inset-block-end: auto" in flow_toc,
"fixed TOC must avoid the visible footer while flow keeps its sticky boundary", errors)
require("@media (min-width: 768px) and (hover: hover) and (pointer: fine)" in navbar_styles,
"drawer-width viewports no longer disable auto-hide", errors)
require("@media (max-width: 767.98px)" in navbar_styles
Expand Down
2 changes: 1 addition & 1 deletion hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ mediaTypes:
module:
hugoVersion:
extended: true
min: 0.166.0
min: 0.168.0
mounts:
- source: assets
target: assets
Expand Down
12 changes: 8 additions & 4 deletions layouts/_partials/head-css.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
{{ $scssMain := "scss/main.scss" -}}
{{ $scss := resources.Get $scssMain
| toCSS (dict "enableSourceMap" (not hugo.IsProduction)) -}}
{{/* Font Awesome 7's source SCSS requires Dart Sass, while Hugo Extended
uses LibSass. Concatenate its official compiled CSS into one stable vendor
{{/* Bootstrap 5.3 and the public project-variable hooks still share Sass's
legacy import scope. Silence only their known language deprecations;
Sass @warn, invalid selectors, and Hugo diagnostics remain visible. */ -}}
{{ $scss := resources.Get $scssMain | toCSS (dict
"transpiler" "dartsass"
"silenceDeprecations" (slice "import" "global-builtin" "color-functions" "if-function")
"enableSourceMap" (not hugo.IsProduction)) -}}
{{/* Concatenate Font Awesome's official compiled CSS into one stable vendor
resource. Keep it separate from consumer SCSS so an ordinary theme or site
style edit does not invalidate the icon distribution. The target remains
under scss/ so Font Awesome's ../webfonts URLs keep their public shape. */ -}}
Expand Down
2 changes: 1 addition & 1 deletion layouts/_partials/scripts.html
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@
{{ end -}}

{{ if and $interactiveOutput (.Page.Store.Get "hasRedoc") -}}
{{ $redocCSS := resources.Get "scss/redoc.scss" | toCSS (dict "enableSourceMap" (not hugo.IsProduction)) -}}
{{ $redocCSS := resources.Get "scss/redoc.scss" | toCSS (dict "transpiler" "dartsass" "enableSourceMap" (not hugo.IsProduction)) -}}
{{ if hugo.IsProduction }}{{ $redocCSS = $redocCSS | minify | fingerprint }}{{ end -}}
<link rel="stylesheet" href="{{ $redocCSS.RelPermalink }}"
{{- with $redocCSS.Data.Integrity }} integrity="{{ . }}" crossorigin="anonymous"{{ end }}>
Expand Down
7 changes: 7 additions & 0 deletions tests/goldens/html/authors-index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,13 @@
id="m-fixturesrelease-assets-base"><span>Release assets with an explicit base</span></a>
</div>
</li><li class="td-shell-tree__item td-leaf"
id="m-fixturestable-layout-policy-li">
<div class="td-shell-tree__row">
<a href="/fixtures/table-layout-policy/" title="Table layout policy"
class="td-shell-tree__link"
id="m-fixturestable-layout-policy"><span>Table layout policy</span></a>
</div>
</li><li class="td-shell-tree__item td-leaf"
id="m-fixturesdownload-pending-li">
<div class="td-shell-tree__row">
<a href="/fixtures/download-pending/" title="Unpublished download demo"
Expand Down
Loading
Loading