Skip to content

feat(toc): landing-page table of contents matching the lecture builds - #240

Open
DrDrij wants to merge 4 commits into
mainfrom
feature/landing-toc
Open

DrDrij wants to merge 4 commits into
mainfrom
feature/landing-toc

Conversation

@DrDrij

@DrDrij DrDrij commented Sep 14, 2026

Copy link
Copy Markdown
Member

Summary

The landing page's {tableofcontents} currently renders as a plain bulleted list: section titles are bare <li> text nodes with a nested list under them, and entries read "1 About These Lectures" — the CLI bakes the enumerator into the text with no period. The existing Sphinx lecture sites render section captions as headings over bulletless lists with "1. Title" entries.

This intercepts the toc:project block (the only marker the CLI's buildTocTransform leaves) and rebuilds the TOC from the site manifest, where enumerator is a separate field — no string surgery on "1 Title" text, so a lecture titled "2008 Financial Crisis" can never be mangled into "2008. Financial Crisis".

Rendering

  • Section titles render as real <h2> headings inside <nav aria-label="Table of contents"> — a deliberate semantic improvement over the old theme's <p role="heading" aria-level="2">. Each carries a stable, deduplicated anchor id (via createHtmlId) and the .heading-text span, so sections join the "On this page" outline and are deep-linkable. (.exclude-from-outline remains a one-line opt-out if a quiet outline is preferred.)
  • Entries render as 1. Title — enumerator, period, space, title; pages without an enumerator render as the bare title.
  • Lists are bulletless and un-indented at the top level, 1.5em per nested level (logical properties, so RTL mirrors).
  • Sizes were measured on the live python.quantecon.org (2026-09-14): links compute to 19.8px (1.1em) at every depth, captions to 21.6px — this ships the same 1.1em/1.2em against the theme's 18px content base. Link colour rides the --qe-link-* tokens; underline on hover/focus-visible only; dark mode inherits the existing white-anchor painting.
  • If the manifest is unavailable in a render context, the block falls back to the CLI's baked list rather than rendering a hole.

Files

  • app/tocTree.ts — pure helpers (nesting, index-page exclusion, id dedupe, labels), unit-testable under node --test.
  • app/components/ProjectTOC.tsx — the renderer, mirroring ContentsSidebar.tsx's manifest access.
  • app/renderers.tsx / app/root.tsxTOC_RENDERERS wraps the base block renderer (selector keys cannot reach data.part); every other block delegates to upstream untouched, including toc:children/toc:page/toc:section.
  • styles/front-toc.css — imported from styles/app.css.
  • Fixture: fixture-no-thebe gains a nested toc group and the directive on its landing page. outline.md deliberately stays top-level — nesting it switches its heading enumerators to the title-prefixed "2.1." shape and would break outline-pinned-and-nested's section-only expectations (commented in the fixture).
  • Registered in UPSTREAM-CANDIDATES.yml (project-toc-renderer): upstream myst-theme has no TOC renderer at all.

Testing

  • tests/unit/toc-tree.test.mjs — 10 tests (nesting, index exclusion, duplicate-title id dedupe, "1. Title" formatting, number-leading titles untouched, empty-tree fallback); verified the period test goes red when the period is dropped.
  • theme.spec.ts: front-toc-structure (real <h2> with id in the labelled nav, "1. " label, bare title for un-enumerated pages, list-style-type: none, no self-link), front-toc-in-outline (the section h2 lands in "On this page" with a working anchor), and one front-toc.png screenshot of the no-thebe landing page, pinned to the outline's settled state.
  • Full local runs: unit 69/69, visual 55 passed / 16 skipped / 0 failed; no existing baseline changed (darwin). Linux baselines for the new screenshot need the usual CI refresh.
  • Manually reviewed against a local myst build of lecture-python.myst (light + dark).

…uilds

The CLI flattens {tableofcontents} into a plain bulleted list: section
titles as bare text list items, entries as "1 Title" (enumerator baked
in, no period). Intercept the toc:project block and rebuild the TOC from
the site manifest instead, where the enumerator is a separate field:
section titles render as real <h2> headings (stable anchor ids, listed in
the On this page outline) inside a nav labelled "Table of contents",
entries as "1. Title", lists bulletless.

Sizes match the live python.quantecon.org toctree, measured 2026-09-14:
links 1.1em (19.8px) at every depth, captions 1.2em serif (21.6px).

The no-thebe fixture gains a nested toc group and the directive on its
landing page; outline.md deliberately stays top-level so its section-only
enumerator expectations in outline-pinned-and-nested keep holding.

New tests: toc-tree unit suite (nesting, index exclusion, id dedupe,
label formatting), front-toc-structure / front-toc-in-outline DOM
assertions, and a front-toc screenshot of the no-thebe landing page.
@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://QuantEcon.github.io/quantecon-theme.mystmd/pr-preview/pr-240/

Built to branch gh-pages at 2026-09-14 22:38 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

🎭 Visual regression results

passed  57 passed
skipped  20 skipped

Details

stats  77 tests across 2 suites
duration  2 minutes
commit  d711c81

Skipped tests

mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › without JavaScript › drawer-opens-without-javascript
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › drawer-closes-when-search-opens
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › live-compute-toggle
mobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › live-compute-toggle-absent-without-thebe
mobile-chrome › theme.spec.ts › On this page outline › outline-pinned-and-nested
mobile-chrome › theme.spec.ts › On this page outline › outline-tracks-scroll
mobile-chrome › theme.spec.ts › On this page outline › outline-unnumbered
mobile-chrome › theme.spec.ts › On this page outline › outline-within-viewport
mobile-chrome › theme.spec.ts › Meta/SEO and notebook output › social-meta
mobile-chrome › theme.spec.ts › Meta/SEO and notebook output › no-canonical-without-site-url
mobile-chrome › theme.spec.ts › Meta/SEO and notebook output › stderr-collapsed
mobile-chrome › theme.spec.ts › Site options reach the theme › site-options
mobile-chrome › theme.spec.ts › Site footer › default-footer-without-part
mobile-chrome › theme.spec.ts › Site footer › declared-part-replaces-default
mobile-chrome › theme.spec.ts › Site footer › footer-contrast
mobile-chrome › theme.spec.ts › Content typography › code-block-size
mobile-chrome › theme.spec.ts › Content typography › callout-size
mobile-chrome › theme.spec.ts › Content typography › link-underline-solid
mobile-chrome › theme.spec.ts › Landing-page table of contents › front-toc-in-outline

mmcky and others added 2 commits September 15, 2026 08:30
…ument the block

The item top margin was 0.5rem, 8px against this theme's 16px root where
the lecture builds' 0.5rem of an 18px root is 9px; it is now 0.5em. The
typography plugin pads every li by 0.375em where the builds' items sit
flush, so the li rule zeroes padding-inline-start. Darwin baselines for
front-toc.png re-recorded for the two changes.

CHANGELOG gains the PR link and UPSTREAM-CANDIDATES the local PR number.
docs/layout.md documents the landing-page table of contents and notes
that its section titles are listed in "On this page".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@mmcky mmcky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @DrDrij, this is a good piece of work: rebuilding from the manifest instead of re-parsing the baked list is the right call, the .heading-text contract with the outline is correctly observed, and the unit coverage is exactly what the tree code needs. I verified the upstream contracts it relies on (data.part === 'toc:project' from the toc transform, the manifest's separate enumerator field, and the outline's main h1..h4 selector), ran typecheck and unit locally, and checked the preview against the live Sphinx sites.

What I pushed to the branch (merge of main plus one commit, no rewrite of your commit):

  • Merged main to clear the CHANGELOG.md conflict with #238, and added the PR link the changelog convention wants; UPSTREAM-CANDIDATES.yml now says local_pr: 240.
  • Two list values that missed the builds by a few pixels. The item top margin was 0.5rem, which is 8px against this theme's 16px root, where the builds' 0.5rem of an 18px root is 9px, so it is now 0.5em. And the typography plugin pads every li by 0.375em (6.75px here) where the builds' items sit flush, so the li rule now zeroes padding-inline-start. Both are in styles/front-toc.css with the derivation in the comment.
  • Re-recorded the two -darwin front-toc.png baselines for those pixel changes.
  • Documented the block in docs/layout.md (a short "Landing-page table of contents" section, plus a line under "On this page" saying the TOC's section titles are listed there), since no docs page mentioned it.

One thing for you to decide: the caption size. The comment in front-toc.css says the captions measure 21.6px in serif on python.quantecon.org. I measured the .toctree-wrapper p.caption on python.quantecon.org, python-programming.quantecon.org and julia.quantecon.org today and all three give the same numbers:

Property Live Sphinx sites This PR
Caption size 27px (1.5em) 21.6px (1.2em)
Caption font Source Sans Pro, weight 400 PT Serif, weight 400
Caption top margin 27px 21.6px

Rendering the caption at the theme's h4 step in PT Serif is a defensible choice, since it keeps the theme's own heading voice rather than the pydata caption's, but the comment presents it as a measurement and it is not one. Either match the builds (1.5em, sans, 1.5em top margin) or keep the serif look and rewrite the comment to give the actual reason. I have left the value and the comment alone so the choice is yours; if you change the size the darwin baselines will need re-recording once more.

Smaller points, your call:

  • Each <section aria-labelledby> is a region landmark, so the lpp landing page now has six regions inside the nav and python.quantecon.org would have around ten. That is a lot of landmark noise for screen-reader users, and the h2 already gives the section its structure. I would drop aria-labelledby (or use a plain div).
  • The directive's :depth: option survives the transform on the node, but the renderer rebuilds from the full manifest and ignores it. And a url: entry in project.toc has no slug, so buildTocTree would render it as a section heading rather than an external link. No lecture repo uses either today, so a follow-up issue is fine if you would rather not handle them here.
  • Note for reviewers: the preview builds lpp's Sphinx main with numbering off, so it exercises the sections and bulletless lists but never the "1. Title" labels. Only the no-thebe fixture test covers those.

Remaining before merge: the -linux baselines for the new screenshot need recording. I will post /update-new-snapshots on this PR once the pushed commit's CI has run, and the refresh commit's gated runs then need approving. Copilot has not reviewed this PR yet, unlike #236, #238 and #239, so a manual review request is worth doing before merge.

@mmcky

mmcky commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

/update-new-snapshots

@github-actions

Copy link
Copy Markdown
Contributor

🎭 Refreshed visual baselines in d711c81:

  • tests/visual/snapshots/desktop-chrome-linux/front-toc.png
  • tests/visual/snapshots/mobile-chrome-linux/front-toc.png

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants