Static blog site generator in one Go binary: Markdown in, self-contained semantic HTML out. No JavaScript, no database, minimal deps.

Optional per-post table of contents (toc: true) #23

Merged janantos.tngl.sh
targeting main from optional-toc
Merged opened by and assigned to janantos.tngl.sh
targeting main from optional-toc

Feature request #20: a frontmatter flag that adds a table of contents to selected posts.

  • frontmatter toc: true (default false; true/1/yes accepted, like draft) — absent posts are byte-identical to before
  • post.TOCFromHTML extracts the h2/h3 headings from the rendered HTML (with the ids goldmark generates, so anchors land exactly on the headings) and returns a nested : h3s under their h2, an h3 before the first h2 at top level, inline tags stripped, entities round-tripped. No h2/h3 -> empty string -> nothing rendered. Code blocks cannot false-positive (goldmark escapes them); raw-HTML headings without an id are skipped (no anchor to point at). No JS.
  • build inserts the nav between the article header and the body, for posts and pages alike; the TOC is page chrome, so feeds keep clean content
  • .toc CSS: left-ruled contents box, Contents label on top inside the rule, entries are plain links (global link styles, like tags). The header link row's flex rule is scoped to header > nav — a bare nav selector would lay the TOC label out beside the list (regression test TestHeaderNavScopedToSiteHeader); the archive year nav gets an explicit .years class for the same rule.
  • anchor jumps clear the sticky header (existing scroll-margin-top)
  • tests: extractor (nesting, entities, code, id-less, early h3, empty), frontmatter parsing, page-level presence/absence and placement, header-nav scoping
  • README (frontmatter + Components), AGENTS.md, showcase post 2026-10-29-toc-without-javascript.md; site rebuilt
Commits 0