Three independent CI failures from run 25916500415:
1. Smoke test [linkToSection aria-label] fail on Hugo 0.146 leg.
The assertion pinned to $post selected via `find ... | head -1`,
which on 0.146 picked `doc-sach-quan-sat/index.html` — a post with
zero h2/h3/h4 headings, so zero heading-anchor markup. Rewrite the
check to scan all built post HTML (same pattern already in use for
render-link rel). Now reports 13 matches on the demo build.
2. htmltest URLSwap silently never matched. The prior
`^/tsuki/: /` config (added in d30f50f) used a regex anchor that
doesn't match htmltest's substring evaluation against minified
`href=/tsuki/...` attributes. Switch to the unanchored
`"/tsuki/": /` form. CI has been failing on this since v0.2.0
despite the "fix" commit; this is the real fix.
3. head.html referenced `/favicon.ico` and `/favicon.svg` but neither
asset existed in exampleSite/static/. Add a minimal SVG favicon,
drop the legacy .ico reference (modern browsers fall back to a
site-rooted /favicon.ico by convention; sites needing one can
override head.html). Switch the SVG link to `relURL` so the
`/tsuki/` baseURL prefix is honoured under GitHub Pages subpath
deploy.
Local validation: all 32 smoke checks green; favicon resolves to
`/tsuki/favicon.svg` and ships in the build artifact.
Headline outcomes:
- Feature parity with Stack/PaperMod on Tier A polish: breadcrumbs
(+BreadcrumbList JSON-LD), prev/next post navigation (rel=prev/next),
language switcher UI (gated on hugo.IsMultilingual), code-copy button
polish, <details> styling.
- Per-page-kind CSS bundles: core loads everywhere; home/single/archive/
search bundles load only where needed. Frees ~1 KB gz from non-post
pages. code-copy.js gated to post pages.
- Lighthouse-relevant network polish: Pagefind UI CSS preload-swap,
conditional preconnect to giscus.app on comment-enabled posts,
hreflang alternates on multilingual sites, <meta name=theme-color>
light/dark variants, aria-pressed SSR-rendered on theme-toggle.
- WCAG AA contrast: --tsuki-fg-subtle darkened to #6b6b6b (light); was
3.54:1 on bg, now 5:1. Pagination disabled uses --tsuki-fg-muted
without opacity compound. Header tap targets bumped to 40×40; pagination
to 44×44.
- i18n: full i18n/en.yml mirror (~50 keys); render-heading aria-label
i18n-driven via linkToSection key; new keys for breadcrumb*, prevPost,
nextPost, copyCode, copiedCode.
- Discovery: /llm.txt output format (llmstxt.org) on home; Speculation
Rules opt-in via params.prefetch.enable.
- <html lang> fallback: site.Language.LanguageCode → Lang → "en"
(was hard-coded "vi").
Per-kind bundle gz sizes on demo: home 3673 / post 4167 / list 2897 /
archives 3363 / search 3179 B (all under 4200 B cap).
Plan: plans/260510-0144-tsuki-v0.3.0/
- search/list.html: Pagefind UI container + module init with full vi
i18n strings; loads /pagefind/pagefind-ui.{css,js} from generated dir
- search-button.html in header, gated by params.search.enable
- comments.html for Giscus, opt-in via params.comments.giscus.enable;
reads repo/repoId/category/categoryId/mapping/theme/lang from params
- giscus-theme.js: MutationObserver on data-theme, postMessage iframe
on toggle so Giscus theme stays in sync with site
- search.css: header-actions row, search button, Pagefind UI tokens
mapped to tsuki design tokens
- comments.css: min-block-size on iframe to prevent layout shift
- exampleSite/hugo.yaml: commented giscus params block as docs
- i18n/vi.yml: Pagefind UI strings (clear, loadMore, zeroResults...)
Both features no-op when disabled. CSS bundle 3.5 KB gz (budget 15).
- post/list.html paginated section list with post-card
- pagination.html partial (renders only when >1 page)
- _default/taxonomy.html for term lists (/tags/, /categories/) with counts
- taxonomy/term.html for single term pages (paginated posts)
- archives/list.html + archive-group.html: year/month grouped flat list
- exampleSite: 4 new posts across 2025-2026, 2 categories, 4 tags, archive index
Modern Hugo 0.146+ template naming: term.html resolved via taxonomy/
path (Hugo 0.154 lookup ignores _default/term.html).