Beta',
diff --git a/bin/check-table-layout-browser.py b/bin/check-table-layout-browser.py
deleted file mode 100644
index b049c0a..0000000
--- a/bin/check-table-layout-browser.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python3
-"""Verify native table geometry in the built bilingual Hugo fixture (no JS deps)."""
-import argparse
-import json
-from functools import partial
-from html.parser import HTMLParser
-from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
-from pathlib import Path
-import subprocess
-import tempfile
-from threading import Thread
-
-
-PROBE = r''''''
-
-
-class Results(HTMLParser):
- def __init__(self):
- super().__init__()
- self.record = False
- self.value = ''
-
- def handle_starttag(self, tag, attrs):
- if tag == 'output' and ('id', 'oink-table-probe') in attrs:
- self.record = True
-
- def handle_data(self, data):
- if self.record:
- self.value += data
-
- def handle_endtag(self, tag):
- if tag == 'output':
- self.record = False
-
-
-class FixtureHandler(SimpleHTTPRequestHandler):
- def do_GET(self):
- if self.path.split('?')[0].rstrip('/') in (
- '/fixtures/table-layout-policy', '/zh/fixtures/table-layout-policy'
- ):
- page = Path(self.translate_path(self.path)) / 'index.html'
- if not page.is_file():
- self.send_error(404)
- return
- html = page.read_text(encoding='utf-8')
- if '' not in html:
- self.send_error(500, 'missing closing body')
- return
- encoded = html.replace('', PROBE + '
' not in html:
+ self.send_error(500, 'missing closing body')
+ return
+ encoded = html.replace('', PROBE + '', 1).encode('utf-8')
+ self.send_response(200)
+ self.send_header('Content-Type', 'text/html; charset=utf-8')
+ self.send_header('Content-Length', str(len(encoded)))
+ self.end_headers()
+ self.wfile.write(encoded)
+ else:
+ super().do_GET()
+
+ def log_message(self, *_):
+ pass
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--public', required=True, type=Path)
+ parser.add_argument('--chrome', required=True)
+ args = parser.parse_args()
+ with ThreadingHTTPServer(('127.0.0.1', 0), partial(
+ FixtureHandler, directory=str(args.public.resolve()))) as server:
+ thread = Thread(target=server.serve_forever, daemon=True)
+ thread.start()
+ try:
+ for language in ('', '/zh'):
+ for width in (1440, 390):
+ with tempfile.TemporaryDirectory(prefix='oink-table-chrome-') as profile:
+ url = (f'http://127.0.0.1:{server.server_port}{language}'
+ '/fixtures/table-layout-policy/')
+ cmd = [args.chrome, '--headless', '--dump-dom',
+ '--disable-background-networking', '--disable-extensions',
+ '--no-first-run', '--no-default-browser-check',
+ '--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1',
+ '--virtual-time-budget=3000', f'--user-data-dir={profile}',
+ f'--window-size={width},1000', url]
+ run = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
+ result = Results()
+ result.feed(run.stdout)
+ if run.returncode != 0 or not result.value:
+ raise RuntimeError(f'Chrome did not evaluate {url} ({width}px): '
+ f'{run.returncode}; {run.stderr[-400:]}')
+ report = json.loads(result.value)
+ if report['problems']:
+ raise AssertionError(f'{language or "en"}/{width}px: {report}')
+ print(f'{language or "en"}/{width}px: passed, '
+ f'key {report["column"]:.1f}px, '
+ f'description {report["description"]:.1f}px')
+ finally:
+ server.shutdown()
+ thread.join(timeout=5)
+ return 0
+
+
+if __name__ == '__main__':
+ raise SystemExit(main())
diff --git a/tests/js/table-layout-source.test.js b/tests/js/table-layout-source.test.js
index 07be387..b82bbc7 100644
--- a/tests/js/table-layout-source.test.js
+++ b/tests/js/table-layout-source.test.js
@@ -1,7 +1,7 @@
'use strict';
-// Deterministic source guard, not browser layout acceptance. The actual
-// prose/technical geometry is checked in the sibling documentation site.
+// Deterministic source guard; native browser geometry is checked by
+// bin/check-table-layout-browser.py against the built fixtures.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
@@ -26,13 +26,25 @@ function blockAfter(text, marker) {
const viewport = blockAfter(source, '.td-table-scroll {');
const screen = blockAfter(viewport, '@media screen');
const policy = blockAfter(screen, '&:where(:not(.td-table-scroll--static)) > table');
+const widthIs = value => new RegExp(`(?:^|[;{])\\s*width:\\s*${value};`, 'm');
test('ordinary screen tables prefer fitting; wide mode is explicit', () => {
const top = policy.slice(0, policy.indexOf('&:where(.td-table--wide)'));
- assert.match(top, /\bwidth:\s*auto;/);
+ assert.match(top, widthIs('auto'));
assert.match(top, /\bmax-width:\s*none;/);
- assert.doesNotMatch(top, /\bwidth:\s*max-content;/);
- assert.match(blockAfter(policy, '&:where(.td-table--wide)'), /\bwidth:\s*max-content;/);
+ assert.doesNotMatch(top, widthIs('max-content'));
+ assert.match(blockAfter(policy, '&:where(.td-table--wide)'), widthIs('max-content'));
+});
+
+test('width declarations cannot be spoofed by min-/max-width', () => {
+ const top = policy.slice(0, policy.indexOf('&:where(.td-table--wide)'));
+ const brokenTop = top.replace(/(^\s*)width:\s*auto;/m, '$1min-width: auto;');
+ assert.notEqual(brokenTop, top);
+ assert.doesNotMatch(brokenTop, widthIs('auto'));
+ const wide = blockAfter(policy, '&:where(.td-table--wide)');
+ const brokenWide = wide.replace(/(^\s*)width:\s*max-content;/m, '$1max-width: max-content;');
+ assert.notEqual(brokenWide, wide);
+ assert.doesNotMatch(brokenWide, widthIs('max-content'));
});
test('both layout policies preserve wrapping and the scrolling viewport', () => {
diff --git a/tests/site/content/fixtures/table-layout-policy.md b/tests/site/content/fixtures/table-layout-policy.md
index dd08a34..c252124 100644
--- a/tests/site/content/fixtures/table-layout-policy.md
+++ b/tests/site/content/fixtures/table-layout-policy.md
@@ -20,6 +20,14 @@ type: docs
| `HG_PD_ACTUATOR_EXPOSURE` | No | `health,metrics,prometheus` | `management.endpoints.web.exposure.include` | Public Actuator endpoint allowlist; `*` is forbidden. |
{#wide-technical-table .td-table--wide}
+## Three-column configuration identifiers
+
+| config option | default value | description |
+| --- | --- | --- |
+| rocksdb.data_path | `rocksdb-data/data` | RocksDB data directory; must not be empty. |
+| rocksdb.data_disks | `[]` | Assign separate disks using STORE/TABLE: /path/disk. Supported keys include g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, and g/olap. Do not reuse rocksdb.data_path. |
+{#configuration-keys-table .td-table--wide}
+
## Short ordinary table
| City | Port | Purpose |
diff --git a/tests/site/content/fixtures/table-layout-policy.zh.md b/tests/site/content/fixtures/table-layout-policy.zh.md
index cabdd5e..c49e9bb 100644
--- a/tests/site/content/fixtures/table-layout-policy.zh.md
+++ b/tests/site/content/fixtures/table-layout-policy.zh.md
@@ -20,6 +20,14 @@ type: docs
| `HG_PD_ACTUATOR_EXPOSURE` | 否 | `health,metrics,prometheus` | `management.endpoints.web.exposure.include` | 对外开放的监控端点白名单,不允许使用通配符。 |
{#wide-technical-table .td-table--wide}
+## 三列配置标识符
+
+| 配置项 | 默认值 | 说明 |
+| --- | --- | --- |
+| rocksdb.data_path | `rocksdb-data/data` | RocksDB 数据目录,不能为空。 |
+| rocksdb.data_disks | `[]` | 使用 STORE/TABLE: /path/disk 指定不同表的数据目录。支持的键包括 g/vertex、g/edge_out、g/edge_in、g/vertex_label_index、g/edge_label_index、g/range_int_index、g/range_float_index、g/range_long_index、g/range_double_index、g/secondary_index、g/search_index、g/shard_index、g/unique_index 和 g/olap。不能复用 rocksdb.data_path。 |
+{#configuration-keys-table .td-table--wide}
+
## 短普通表格
| 城市 | 端口 | 用途 |
From 548e36d001212b4ebda1f05b654dae1d99c259b2 Mon Sep 17 00:00:00 2001
From: imbajin
Date: Fri, 16 Oct 2026 16:27:35 +0800
Subject: [PATCH 12/18] fix(toc): refresh footer offset after tab changes
---
assets/js/docs-shell.js | 1 +
tests/js/docs-shell-footer.test.js | 27 +++++++++++++++++++++++++--
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/assets/js/docs-shell.js b/assets/js/docs-shell.js
index 67f9e35..c3417f5 100644
--- a/assets/js/docs-shell.js
+++ b/assets/js/docs-shell.js
@@ -161,6 +161,7 @@
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,
diff --git a/tests/js/docs-shell-footer.test.js b/tests/js/docs-shell-footer.test.js
index 8c95c1c..7f6a23e 100644
--- a/tests/js/docs-shell-footer.test.js
+++ b/tests/js/docs-shell-footer.test.js
@@ -18,6 +18,7 @@ function build({ footer = true, sidebar = true, toc = true, visual = false, top
const properties = new Map();
const attrs = new Map();
const listeners = new Map();
+ const documentListeners = new Map();
const viewportListeners = new Map();
const frames = new Map();
const measurements = { top, reads: 0 };
@@ -52,7 +53,11 @@ function build({ footer = true, sidebar = true, toc = true, visual = false, top
},
querySelectorAll() { return []; },
getElementById() { return null; },
- addEventListener() {},
+ addEventListener(name, callback) { documentListeners.set(name, callback); },
+ dispatchEvent(event) {
+ const callback = documentListeners.get(event.type);
+ if (callback) callback(event);
+ },
};
const window = {
innerHeight: 1000,
@@ -89,7 +94,7 @@ function build({ footer = true, sidebar = true, toc = true, visual = false, top
};
flush();
return {
- window, measurements, listeners, viewportListeners, flush,
+ window, document, measurements, listeners, viewportListeners, flush,
resize() { resize(); },
offset() { return properties.get('--td-shell-footer-offset'); },
};
@@ -112,6 +117,23 @@ for (const rails of [
env.flush();
assert.equal(env.offset(), '0px');
});
+
+ test(`tab reflow refreshes the footer boundary without scrolling with ${JSON.stringify(rails)}`, () => {
+ const env = build({ ...rails, top: 670 });
+ assert.equal(env.offset(), '331px');
+ const reads = env.measurements.reads;
+
+ env.measurements.top = 1200;
+ env.document.dispatchEvent({ type: 'td:tab-shown' });
+ assert.equal(env.measurements.reads, reads);
+ env.flush();
+ assert.equal(env.offset(), '0px');
+
+ env.measurements.top = 670;
+ env.document.dispatchEvent({ type: 'td:tab-shown' });
+ env.flush();
+ assert.equal(env.offset(), '331px');
+ });
}
test('scroll bursts share one measurement and a resized footer is observed', () => {
@@ -183,6 +205,7 @@ test('mixed observer, window and visual viewport events share one frame', () =>
env.listeners.get('resize')();
env.viewportListeners.get('scroll')();
env.viewportListeners.get('resize')();
+ env.document.dispatchEvent({ type: 'td:tab-shown' });
env.resize();
assert.equal(env.measurements.reads, reads);
env.flush();
From 56da1aaf418d9c1e72d78ae6c2335011423bad5e Mon Sep 17 00:00:00 2001
From: imbajin
Date: Sat, 17 Oct 2026 11:40:19 +0800
Subject: [PATCH 13/18] fix(toc): use layout viewport for footer clearance
---
assets/js/docs-shell.js | 6 ++----
tests/js/docs-shell-footer.test.js | 30 ++++++++++++++++++++++--------
2 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/assets/js/docs-shell.js b/assets/js/docs-shell.js
index c3417f5..e9740f3 100644
--- a/assets/js/docs-shell.js
+++ b/assets/js/docs-shell.js
@@ -145,12 +145,10 @@
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');
diff --git a/tests/js/docs-shell-footer.test.js b/tests/js/docs-shell-footer.test.js
index 7f6a23e..5a3c27b 100644
--- a/tests/js/docs-shell-footer.test.js
+++ b/tests/js/docs-shell-footer.test.js
@@ -73,6 +73,7 @@ function build({ footer = true, sidebar = true, toc = true, visual = false, top
if (visual) {
window.visualViewport = {
height: 800,
+ offsetTop: 0,
addEventListener(name, callback) { viewportListeners.set(name, callback); },
};
}
@@ -157,16 +158,29 @@ test('scroll bursts share one measurement and a resized footer is observed', ()
assert.equal(env.offset(), '0px');
});
-test('the visual viewport owns offset when browser chrome changes its height', () => {
- const env = build({ sidebar: false, visual: true });
- env.measurements.top = 700;
+test('fixed rails use layout coordinates during pinch zoom and visual panning', () => {
+ const env = build({ sidebar: false, visual: true, top: 650 });
+ assert.equal(env.offset(), '351px');
+
+ env.window.visualViewport.height = 500;
+ env.viewportListeners.get('resize')();
+ env.flush();
+ assert.equal(env.offset(), '351px');
+
+ env.window.visualViewport.offsetTop = 300;
env.viewportListeners.get('scroll')();
env.flush();
- assert.equal(env.offset(), '101px');
- env.window.visualViewport.height = 750;
- env.viewportListeners.get('resize')();
+ assert.equal(env.offset(), '351px');
+
+ env.window.innerHeight = 900;
+ env.listeners.get('resize')();
env.flush();
- assert.equal(env.offset(), '51px');
+ assert.equal(env.offset(), '251px');
+
+ env.measurements.top = 1200;
+ env.listeners.get('scroll')();
+ env.flush();
+ assert.equal(env.offset(), '0px');
});
for (const absent of [{ footer: false }, { sidebar: false, toc: false }]) {
@@ -210,5 +224,5 @@ test('mixed observer, window and visual viewport events share one frame', () =>
assert.equal(env.measurements.reads, reads);
env.flush();
assert.equal(env.measurements.reads, reads + 1);
- assert.equal(env.offset(), '101px');
+ assert.equal(env.offset(), '301px');
});
From 8372a676991e59486124c9c6bd4a66d408631734 Mon Sep 17 00:00:00 2001
From: Feng Ruohang
Date: Sat, 17 Oct 2026 22:34:12 +0800
Subject: [PATCH 14/18] build: advance Hugo to 0.168 with standalone Dart Sass
---
.github/workflows/ci.yml | 24 +++++++++++++++++++++++-
AGENTS.md | 4 +++-
CHANGELOG.md | 14 ++++++++++++++
README.md | 14 ++++++++------
assets/scss/td/_chroma.scss | 4 +++-
assets/scss/td/_release.scss | 2 +-
hugo.yaml | 2 +-
layouts/_partials/head-css.html | 12 ++++++++----
layouts/_partials/scripts.html | 2 +-
tests/goldens/html/home-landing-zh.html | 2 +-
tests/goldens/html/home-landing.html | 2 +-
tests/goldens/rss/fixtures.xml | 8 ++++----
theme.toml | 2 +-
13 files changed, 69 insertions(+), 23 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a362524..1461c28 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,7 +11,7 @@ permissions:
env:
GO_VERSION: 1.27.0
- HUGO_VERSION: 0.166.0
+ HUGO_VERSION: 0.168.0
GOTOOLCHAIN: local
jobs:
@@ -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
@@ -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
diff --git a/AGENTS.md b/AGENTS.md
index 8d428d8..565038c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab35f6c..c53e3d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,20 @@ 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.
+
## [1.3.0] — 2026-10-15
Resource-backed Expo albums with shared navigation, an original-image viewer,
diff --git a/README.md b/README.md
index 237e0a2..241a420 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
-
+
@@ -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.
@@ -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
@@ -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
@@ -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 |
diff --git a/assets/scss/td/_chroma.scss b/assets/scss/td/_chroma.scss
index bde0a52..2d92b40 100644
--- a/assets/scss/td/_chroma.scss
+++ b/assets/scss/td/_chroma.scss
@@ -1,3 +1,5 @@
+@use "sass:meta";
+
// Class-based syntax highlighting (Chroma).
//
// Requires `markup.highlight.noClasses: false`, which the theme sets in its
@@ -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(
diff --git a/assets/scss/td/_release.scss b/assets/scss/td/_release.scss
index b37e11f..485e98b 100644
--- a/assets/scss/td/_release.scss
+++ b/assets/scss/td/_release.scss
@@ -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);
}
diff --git a/hugo.yaml b/hugo.yaml
index be99ebc..4bc2822 100644
--- a/hugo.yaml
+++ b/hugo.yaml
@@ -299,7 +299,7 @@ mediaTypes:
module:
hugoVersion:
extended: true
- min: 0.166.0
+ min: 0.168.0
mounts:
- source: assets
target: assets
diff --git a/layouts/_partials/head-css.html b/layouts/_partials/head-css.html
index e4bfa91..ec2f077 100644
--- a/layouts/_partials/head-css.html
+++ b/layouts/_partials/head-css.html
@@ -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. */ -}}
diff --git a/layouts/_partials/scripts.html b/layouts/_partials/scripts.html
index 6d17e66..8058260 100644
--- a/layouts/_partials/scripts.html
+++ b/layouts/_partials/scripts.html
@@ -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 -}}
diff --git a/tests/goldens/html/home-landing-zh.html b/tests/goldens/html/home-landing-zh.html
index 7c89054..74a103c 100644
--- a/tests/goldens/html/home-landing-zh.html
+++ b/tests/goldens/html/home-landing-zh.html
@@ -1,7 +1,6 @@
-
@@ -75,6 +74,7 @@
} catch (_) {}
})();
+
', 1).encode('utf-8')
- self.send_response(200)
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.send_header('Content-Length', str(len(encoded)))
- self.end_headers()
- self.wfile.write(encoded)
- else:
- super().do_GET()
-
- def log_message(self, *_):
- pass
-
-
-def main():
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('--public', required=True, type=Path)
- parser.add_argument('--chrome', required=True)
- args = parser.parse_args()
- with ThreadingHTTPServer(('127.0.0.1', 0), partial(
- FixtureHandler, directory=str(args.public.resolve()))) as server:
- thread = Thread(target=server.serve_forever, daemon=True)
- thread.start()
- try:
- for language in ('', '/zh'):
- for width in (1440, 390):
- with tempfile.TemporaryDirectory(prefix='oink-table-chrome-') as profile:
- url = (f'http://127.0.0.1:{server.server_port}{language}'
- '/fixtures/table-layout-policy/')
- cmd = [args.chrome, '--headless', '--dump-dom',
- '--disable-background-networking', '--disable-extensions',
- '--no-first-run', '--no-default-browser-check',
- '--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1',
- '--virtual-time-budget=3000', f'--user-data-dir={profile}',
- f'--window-size={width},1000', url]
- run = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
- result = Results()
- result.feed(run.stdout)
- if run.returncode != 0 or not result.value:
- raise RuntimeError(f'Chrome did not evaluate {url} ({width}px): '
- f'{run.returncode}; {run.stderr[-400:]}')
- report = json.loads(result.value)
- if report['problems']:
- raise AssertionError(f'{language or "en"}/{width}px: {report}')
- print(f'{language or "en"}/{width}px: passed, '
- f'key {report["column"]:.1f}px, '
- f'description {report["description"]:.1f}px')
- finally:
- server.shutdown()
- thread.join(timeout=5)
- return 0
-
-
-if __name__ == '__main__':
- raise SystemExit(main())
diff --git a/tests/goldens/html/authors-index.html b/tests/goldens/html/authors-index.html
index 30ffb7f..5e281ae 100644
--- a/tests/goldens/html/authors-index.html
+++ b/tests/goldens/html/authors-index.html
@@ -1026,6 +1026,13 @@
id="m-fixturesrelease-assets-base">Release assets with an explicit base
A short explanation of the current delivery status.
+
+
+
Bob handles design and delivery
+
Osaka patch
+
Waiting on tests
+
Before the next release window
+
Another short explanation of the expected next step.
+
+
+
+
+
Explicit technical width
+
+
+
+
+
+
Variable
+
Required
+
Default
+
Configuration key
+
Description
+
+
+
+
+
HG_PD_GRPC_HOST
+
Yes
+
None
+
grpc.host
+
Advertised gRPC hostname/IP; use container hostnames within a container network.
+
+
+
HG_PD_INITIAL_STORE_COUNT
+
No
+
1
+
pd.initial-store-count
+
Minimum Store count needed for cluster availability.
+
+
+
HG_PD_ACTUATOR_EXPOSURE
+
No
+
health,metrics,prometheus
+
management.endpoints.web.exposure.include
+
Public Actuator endpoint allowlist; * is forbidden.
+
+
+
+
+
Three-column configuration identifiers
+
+
+
+
+
+
config option
+
default value
+
description
+
+
+
+
+
rocksdb.data_path
+
rocksdb-data/data
+
RocksDB data directory; must not be empty.
+
+
+
rocksdb.data_disks
+
[]
+
Assign separate disks using STORE/TABLE: /path/disk. Supported keys include g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, and g/olap. Do not reuse rocksdb.data_path.
+
+
+
+
+
Short ordinary table
+
+
+
+
+
+
City
+
Port
+
Purpose
+
+
+
+
+
Tokyo
+
8080
+
Graph API
+
+
+
Osaka
+
8686
+
Metadata
+
+
+
+
+
Explicit width with existing modifiers
+
+
+
+
Existing modifiers remain independent
+
+
+
Component
+
Read
+
Write
+
+
+
+
+
A long component identifier
+
Yes
+
No
+
+
+
+
+
+
+
25 - Unpublished download demo
Pinned release facts remain visible without exposing download actions.
diff --git a/tests/goldens/rss/fixtures.xml b/tests/goldens/rss/fixtures.xml
index 9de206d..946d0f6 100644
--- a/tests/goldens/rss/fixtures.xml
+++ b/tests/goldens/rss/fixtures.xml
@@ -149,6 +149,13 @@
| File | Checksum |
| --- | --- |
| [oink\-linux\-amd64\.tar\.gz](https://downloads.example.org/releases/stable/oink-linux-amd64.tar.gz) | SHA-256 · `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` |
| [OINK manual \(final\)\.zip](https://downloads.example.org/releases/stable/OINK%20manual%20%28final%29.zip) | SHA-256 · `bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb` |
+Table layout policy
+https://example.org/fixtures/table-layout-policy/
+Mon, 01 Jan 0001 00:00:00 +0000
+https://example.org/fixtures/table-layout-policy/
+<h2 id="ordinary-prose">Ordinary prose
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="ordinary-prose-table">
<thead>
<tr>
<th scope="col">Owner</th>
<th scope="col">Project</th>
<th scope="col">State</th>
<th scope="col">When</th>
<th scope="col">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice handles planning and coordination</td>
<td>Tokyo release</td>
<td>Ready for review</td>
<td>After final review and approval</td>
<td>A short explanation of the current delivery status.</td>
</tr>
<tr>
<td>Bob handles design and delivery</td>
<td>Osaka patch</td>
<td>Waiting on tests</td>
<td>Before the next release window</td>
<td>Another short explanation of the expected next step.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-technical-width">Explicit technical width
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="wide-technical-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">Variable</th>
<th scope="col">Required</th>
<th scope="col">Default</th>
<th scope="col">Configuration key</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HG_PD_GRPC_HOST</code></td>
<td>Yes</td>
<td>None</td>
<td><code>grpc.host</code></td>
<td>Advertised gRPC hostname/IP; use container hostnames within a container network.</td>
</tr>
<tr>
<td><code>HG_PD_INITIAL_STORE_COUNT</code></td>
<td>No</td>
<td><code>1</code></td>
<td><code>pd.initial-store-count</code></td>
<td>Minimum Store count needed for cluster availability.</td>
</tr>
<tr>
<td><code>HG_PD_ACTUATOR_EXPOSURE</code></td>
<td>No</td>
<td><code>health,metrics,prometheus</code></td>
<td><code>management.endpoints.web.exposure.include</code></td>
<td>Public Actuator endpoint allowlist; <code>*</code> is forbidden.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="three-column-configuration-identifiers">Three-column configuration identifiers
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="configuration-keys-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">config option</th>
<th scope="col">default value</th>
<th scope="col">description</th>
</tr>
</thead>
<tbody>
<tr>
<td>rocksdb.data_path</td>
<td><code>rocksdb-data/data</code></td>
<td>RocksDB data directory; must not be empty.</td>
</tr>
<tr>
<td>rocksdb.data_disks</td>
<td><code>[]</code></td>
<td>Assign separate disks using STORE/TABLE: /path/disk. Supported keys include g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, and g/olap. Do not reuse rocksdb.data_path.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="short-ordinary-table">Short ordinary table
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="short-ordinary-table">
<thead>
<tr>
<th scope="col">City</th>
<th scope="col">Port</th>
<th scope="col">Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tokyo</td>
<td>8080</td>
<td>Graph API</td>
</tr>
<tr>
<td>Osaka</td>
<td>8686</td>
<td>Metadata</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-width-with-existing-modifiers">Explicit width with existing modifiers
</h2>
<div class="td-table-scroll td-table-scroll--full td-table-scroll--matrix td-table-scroll--static">
<table id="wide-matrix-table" class="matrix full-width td-table--wide td-table--matrix">
<caption class="td-table__caption">Existing modifiers remain independent</caption>
<thead>
<tr>
<th scope="col">Component</th>
<th scope="col">Read</th>
<th scope="col">Write</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">A long component identifier</th>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
</table>
</div>
+
+Unpublished download demo
https://example.org/fixtures/download-pending/
Mon, 01 Jan 0001 00:00:00 +0000
diff --git a/tests/js/table-layout-source.test.js b/tests/js/table-layout-source.test.js
deleted file mode 100644
index b82bbc7..0000000
--- a/tests/js/table-layout-source.test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-'use strict';
-
-// Deterministic source guard; native browser geometry is checked by
-// bin/check-table-layout-browser.py against the built fixtures.
-const assert = require('node:assert/strict');
-const fs = require('node:fs');
-const path = require('node:path');
-const test = require('node:test');
-
-const root = path.join(__dirname, '../..');
-const source = fs.readFileSync(path.join(root, 'assets/scss/td/_content.scss'), 'utf8')
- .replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, '');
-
-function blockAfter(text, marker) {
- const start = text.indexOf(marker);
- assert.notEqual(start, -1, 'missing owning selector: ' + marker);
- const open = text.indexOf('{', start);
- let depth = 1;
- for (let end = open + 1; end < text.length; end++) {
- if (text[end] === '{') depth++;
- if (text[end] === '}' && --depth === 0) return text.slice(open + 1, end);
- }
- assert.fail('unclosed source block: ' + marker);
-}
-
-const viewport = blockAfter(source, '.td-table-scroll {');
-const screen = blockAfter(viewport, '@media screen');
-const policy = blockAfter(screen, '&:where(:not(.td-table-scroll--static)) > table');
-const widthIs = value => new RegExp(`(?:^|[;{])\\s*width:\\s*${value};`, 'm');
-
-test('ordinary screen tables prefer fitting; wide mode is explicit', () => {
- const top = policy.slice(0, policy.indexOf('&:where(.td-table--wide)'));
- assert.match(top, widthIs('auto'));
- assert.match(top, /\bmax-width:\s*none;/);
- assert.doesNotMatch(top, widthIs('max-content'));
- assert.match(blockAfter(policy, '&:where(.td-table--wide)'), widthIs('max-content'));
-});
-
-test('width declarations cannot be spoofed by min-/max-width', () => {
- const top = policy.slice(0, policy.indexOf('&:where(.td-table--wide)'));
- const brokenTop = top.replace(/(^\s*)width:\s*auto;/m, '$1min-width: auto;');
- assert.notEqual(brokenTop, top);
- assert.doesNotMatch(brokenTop, widthIs('auto'));
- const wide = blockAfter(policy, '&:where(.td-table--wide)');
- const brokenWide = wide.replace(/(^\s*)width:\s*max-content;/m, '$1max-width: max-content;');
- assert.notEqual(brokenWide, wide);
- assert.doesNotMatch(brokenWide, widthIs('max-content'));
-});
-
-test('both layout policies preserve wrapping and the scrolling viewport', () => {
- assert.match(policy, /overflow-wrap:\s*break-word;/);
- assert.match(policy, /word-break:\s*normal;/);
- assert.match(policy, /max-width:\s*40ch;/);
- assert.match(viewport, /overflow-x:\s*auto;/);
- assert.match(blockAfter(viewport, '> table {'), /min-width:\s*100%;/);
-});
-
-test('wide mode is assigned only inside the non-static screen policy', () => {
- assert.equal((source.match(/&:where\(\.td-table--wide\)\s*\{/g) || []).length, 1);
- assert.equal((policy.match(/&:where\(\.td-table--wide\)\s*\{/g) || []).length, 1);
-});
-
-test('cell sizing does not leak into nested static tables', () => {
- assert.ok(policy.includes('> :where(thead, tbody, tfoot) > :where(tr) > :is(th, td)'));
- assert.ok(policy.includes('> :where(thead, tbody, tfoot) > :where(tr) > td'));
- assert.match(blockAfter(viewport, '&--static'), /overflow-wrap:\s*anywhere;/);
-});
From ea1d80a4854c47a790b026838f931d66564a38e6 Mon Sep 17 00:00:00 2001
From: Feng Ruohang
Date: Sat, 17 Oct 2026 22:48:50 +0800
Subject: [PATCH 17/18] docs: record the completed readability repairs
---
CHANGELOG.md | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c53e3d7..e174e7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,16 @@ All notable changes to OINK are documented here. The project follows
- 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,
From bf4596ec55a57ec58b0a0eea47590e8e4d1e348e Mon Sep 17 00:00:00 2001
From: Feng Ruohang
Date: Sat, 17 Oct 2026 22:56:25 +0800
Subject: [PATCH 18/18] test: keep technical table fixture anchors unique
---
tests/goldens/print/fixtures-section.html | 2 +-
tests/goldens/rss/fixtures.xml | 2 +-
tests/site/content/fixtures/table-layout-policy.md | 2 +-
tests/site/content/fixtures/table-layout-policy.zh.md | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/goldens/print/fixtures-section.html b/tests/goldens/print/fixtures-section.html
index 85531e9..c4cf4e2 100644
--- a/tests/goldens/print/fixtures-section.html
+++ b/tests/goldens/print/fixtures-section.html
@@ -1652,7 +1652,7 @@
Short ordinary table
-
+
City
diff --git a/tests/goldens/rss/fixtures.xml b/tests/goldens/rss/fixtures.xml
index 946d0f6..344bf59 100644
--- a/tests/goldens/rss/fixtures.xml
+++ b/tests/goldens/rss/fixtures.xml
@@ -153,7 +153,7 @@
https://example.org/fixtures/table-layout-policy/
Mon, 01 Jan 0001 00:00:00 +0000https://example.org/fixtures/table-layout-policy/
-<h2 id="ordinary-prose">Ordinary prose
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="ordinary-prose-table">
<thead>
<tr>
<th scope="col">Owner</th>
<th scope="col">Project</th>
<th scope="col">State</th>
<th scope="col">When</th>
<th scope="col">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice handles planning and coordination</td>
<td>Tokyo release</td>
<td>Ready for review</td>
<td>After final review and approval</td>
<td>A short explanation of the current delivery status.</td>
</tr>
<tr>
<td>Bob handles design and delivery</td>
<td>Osaka patch</td>
<td>Waiting on tests</td>
<td>Before the next release window</td>
<td>Another short explanation of the expected next step.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-technical-width">Explicit technical width
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="wide-technical-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">Variable</th>
<th scope="col">Required</th>
<th scope="col">Default</th>
<th scope="col">Configuration key</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HG_PD_GRPC_HOST</code></td>
<td>Yes</td>
<td>None</td>
<td><code>grpc.host</code></td>
<td>Advertised gRPC hostname/IP; use container hostnames within a container network.</td>
</tr>
<tr>
<td><code>HG_PD_INITIAL_STORE_COUNT</code></td>
<td>No</td>
<td><code>1</code></td>
<td><code>pd.initial-store-count</code></td>
<td>Minimum Store count needed for cluster availability.</td>
</tr>
<tr>
<td><code>HG_PD_ACTUATOR_EXPOSURE</code></td>
<td>No</td>
<td><code>health,metrics,prometheus</code></td>
<td><code>management.endpoints.web.exposure.include</code></td>
<td>Public Actuator endpoint allowlist; <code>*</code> is forbidden.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="three-column-configuration-identifiers">Three-column configuration identifiers
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="configuration-keys-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">config option</th>
<th scope="col">default value</th>
<th scope="col">description</th>
</tr>
</thead>
<tbody>
<tr>
<td>rocksdb.data_path</td>
<td><code>rocksdb-data/data</code></td>
<td>RocksDB data directory; must not be empty.</td>
</tr>
<tr>
<td>rocksdb.data_disks</td>
<td><code>[]</code></td>
<td>Assign separate disks using STORE/TABLE: /path/disk. Supported keys include g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, and g/olap. Do not reuse rocksdb.data_path.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="short-ordinary-table">Short ordinary table
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="short-ordinary-table">
<thead>
<tr>
<th scope="col">City</th>
<th scope="col">Port</th>
<th scope="col">Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tokyo</td>
<td>8080</td>
<td>Graph API</td>
</tr>
<tr>
<td>Osaka</td>
<td>8686</td>
<td>Metadata</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-width-with-existing-modifiers">Explicit width with existing modifiers
</h2>
<div class="td-table-scroll td-table-scroll--full td-table-scroll--matrix td-table-scroll--static">
<table id="wide-matrix-table" class="matrix full-width td-table--wide td-table--matrix">
<caption class="td-table__caption">Existing modifiers remain independent</caption>
<thead>
<tr>
<th scope="col">Component</th>
<th scope="col">Read</th>
<th scope="col">Write</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">A long component identifier</th>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
</table>
</div>
+<h2 id="ordinary-prose">Ordinary prose
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="ordinary-prose-table">
<thead>
<tr>
<th scope="col">Owner</th>
<th scope="col">Project</th>
<th scope="col">State</th>
<th scope="col">When</th>
<th scope="col">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice handles planning and coordination</td>
<td>Tokyo release</td>
<td>Ready for review</td>
<td>After final review and approval</td>
<td>A short explanation of the current delivery status.</td>
</tr>
<tr>
<td>Bob handles design and delivery</td>
<td>Osaka patch</td>
<td>Waiting on tests</td>
<td>Before the next release window</td>
<td>Another short explanation of the expected next step.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-technical-width">Explicit technical width
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="wide-technical-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">Variable</th>
<th scope="col">Required</th>
<th scope="col">Default</th>
<th scope="col">Configuration key</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HG_PD_GRPC_HOST</code></td>
<td>Yes</td>
<td>None</td>
<td><code>grpc.host</code></td>
<td>Advertised gRPC hostname/IP; use container hostnames within a container network.</td>
</tr>
<tr>
<td><code>HG_PD_INITIAL_STORE_COUNT</code></td>
<td>No</td>
<td><code>1</code></td>
<td><code>pd.initial-store-count</code></td>
<td>Minimum Store count needed for cluster availability.</td>
</tr>
<tr>
<td><code>HG_PD_ACTUATOR_EXPOSURE</code></td>
<td>No</td>
<td><code>health,metrics,prometheus</code></td>
<td><code>management.endpoints.web.exposure.include</code></td>
<td>Public Actuator endpoint allowlist; <code>*</code> is forbidden.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="three-column-configuration-identifiers">Three-column configuration identifiers
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="configuration-keys-table" class="td-table--wide">
<thead>
<tr>
<th scope="col">config option</th>
<th scope="col">default value</th>
<th scope="col">description</th>
</tr>
</thead>
<tbody>
<tr>
<td>rocksdb.data_path</td>
<td><code>rocksdb-data/data</code></td>
<td>RocksDB data directory; must not be empty.</td>
</tr>
<tr>
<td>rocksdb.data_disks</td>
<td><code>[]</code></td>
<td>Assign separate disks using STORE/TABLE: /path/disk. Supported keys include g/vertex, g/edge_out, g/edge_in, g/vertex_label_index, g/edge_label_index, g/range_int_index, g/range_float_index, g/range_long_index, g/range_double_index, g/secondary_index, g/search_index, g/shard_index, g/unique_index, and g/olap. Do not reuse rocksdb.data_path.</td>
</tr>
</tbody>
</table>
</div>
<h2 id="short-ordinary-table">Short ordinary table
</h2>
<div class="td-table-scroll td-table-scroll--static">
<table id="short-prose-table">
<thead>
<tr>
<th scope="col">City</th>
<th scope="col">Port</th>
<th scope="col">Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tokyo</td>
<td>8080</td>
<td>Graph API</td>
</tr>
<tr>
<td>Osaka</td>
<td>8686</td>
<td>Metadata</td>
</tr>
</tbody>
</table>
</div>
<h2 id="explicit-width-with-existing-modifiers">Explicit width with existing modifiers
</h2>
<div class="td-table-scroll td-table-scroll--full td-table-scroll--matrix td-table-scroll--static">
<table id="wide-matrix-table" class="matrix full-width td-table--wide td-table--matrix">
<caption class="td-table__caption">Existing modifiers remain independent</caption>
<thead>
<tr>
<th scope="col">Component</th>
<th scope="col">Read</th>
<th scope="col">Write</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">A long component identifier</th>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
</table>
</div>Unpublished download demo
diff --git a/tests/site/content/fixtures/table-layout-policy.md b/tests/site/content/fixtures/table-layout-policy.md
index c252124..81789e1 100644
--- a/tests/site/content/fixtures/table-layout-policy.md
+++ b/tests/site/content/fixtures/table-layout-policy.md
@@ -34,7 +34,7 @@ type: docs
| --- | --- | --- |
| Tokyo | 8080 | Graph API |
| Osaka | 8686 | Metadata |
-{#short-ordinary-table}
+{#short-prose-table}
## Explicit width with existing modifiers
diff --git a/tests/site/content/fixtures/table-layout-policy.zh.md b/tests/site/content/fixtures/table-layout-policy.zh.md
index c49e9bb..e847e35 100644
--- a/tests/site/content/fixtures/table-layout-policy.zh.md
+++ b/tests/site/content/fixtures/table-layout-policy.zh.md
@@ -34,7 +34,7 @@ type: docs
| --- | --- | --- |
| 东京 | 8080 | 图接口 |
| 大阪 | 8686 | 元数据 |
-{#short-ordinary-table}
+{#short-prose-table}
## 显式宽度与已有修饰符