feat: restore the Sphinx lecture heading typography (PT Serif + scale) - #166
Conversation
The Sphinx builds (python-programming.quantecon.org) set h1-h3 in "PT Serif", serif; h4/h5 stay in the sans face there, so the same split applies here. Family only: the typography plugin's heading weights (800/700/600) resolve against PT Serif's 400+700 faces to the same 700 the Sphinx build renders. The face is self-hosted via @fontsource/pt-serif through the same Remix/esbuild pipeline as Source Sans 3, and mirrored in CRITICAL_CSS so the first WebKit paint on static-build navigations is already serif. Verified on both shipping paths (myst start against the theme build, and myst build --html served statically): computed heading font-family is "PT Serif", document.fonts confirms the 700 face loads, and no asset request fails.
The Sphinx builds size content headings at h1 2em of the body, then h2 1.7rem / h3 1.4rem / h4 1.2rem / h5 1rem of an 18px desktop root -- the same 18px this theme sets on .article, so each value carries over as the same number in em and the scale keeps tracking the content size. h5 needs no rule: the typography plugin already leaves it at 1em. Mirrored in CRITICAL_CSS: beyond avoiding a reflow when the bundle lands, the UA default for an h1 inside <article> is 1.5em, so the first WebKit paint would otherwise draw it visibly small. Verified against the theme build on the visual fixture: computed sizes are 36 / 30.6 / 25.2 / 21.6 / 18px across h1-h5 (h3-h5 probed via injected elements; the fixture has no such headings), families unchanged (h1-h3 PT Serif, h4-h5 sans).
Measured computed on the live Sphinx build: every heading level renders at line-height 1.15x its font-size, replacing the plugin's per-level values (1.11/1.33/1.6/1.5, and prose's 1.75 on the unstyled h5). h4/h5 also take the Sphinx build's font-weight 900. Unlike h1-h3, where any weight from 600 up resolves to PT Serif's single 700 face and the plugin's values already render identically, the sans is a variable font with a real weight axis -- the plugin's 600 (h4) and unstyled 400 (h5) drew visibly lighter. Both mirrored in CRITICAL_CSS. Verified against the theme build on the visual fixture: computed line-heights are 41.4/35.19/28.98/24.84/20.7px across h1-h5 and h4/h5 report weight 900, matching the live-site measurements exactly.
Every page snapshot moves with the PT Serif face and the new heading scale, line-height and h4/h5 weight. The linux baselines are refreshed in CI via the /update-snapshots workflow once a PR is open.
|
🎭 Visual regression resultsDetails
Skipped testsmobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › without JavaScript › drawer-opens-without-javascript |
|
/update-snapshots |
|
🎭 Refreshed visual baselines in 626ace8:
|
There was a problem hiding this comment.
🟡 Changes recommended
The change omits the required ## [Unreleased] CHANGELOG.md entry mandated by CONTRIBUTING.md for feature and dependency changes.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR restores the heading typography of QuantEcon's Sphinx lecture builds in the MyST theme, which previously fell back to the Tailwind typography plugin's defaults. It self-hosts the PT Serif face (via @fontsource/pt-serif, routed through the existing Remix/esbuild pipeline like Source Sans 3), applies it to .article h1/h2/h3, and matches the Sphinx content heading scale, line-height, and h4/h5 weight. All heading rules are mirrored into CRITICAL_CSS so the first WebKit paint on static-build navigations already renders serif at the final sizes, avoiding a small/reflowing flash.
Changes:
- Add self-hosted PT Serif (
@fontsource/pt-serif, 400/700 upright+italic) and aPTSerifCSSlink export, loaded on the root route so error pages get it too. - Add heading face/scale/line-height/weight rules to
styles/quantecon.css(@layer base), documented with specificity reasoning. - Mirror the heading rules into
CRITICAL_CSSinapp/root.tsx, with the sync-source comment extended.
File summaries
| File | Description |
|---|---|
styles/quantecon.css |
Adds .article heading font-family, font-size scale, line-height, and h4/h5 weight rules matched to the Sphinx build. |
app/root.tsx |
Imports PTSerifCSS, adds heading rules to CRITICAL_CSS, loads PT Serif on the root route, and updates the sync-source comment. |
app/links.ts |
Adds the PTSerifCSS export importing the four @fontsource/pt-serif weight/style stylesheets. |
package.json |
Adds the @fontsource/pt-serif dependency. |
package-lock.json |
Locks @fontsource/pt-serif@5.3.0 (and reconciles the pre-existing prettier spec). |
Review details
- Files reviewed: 4/30 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…pt-serif # Conflicts: # package-lock.json # package.json # tests/visual/__snapshots__/desktop-chrome-darwin/features.png # tests/visual/__snapshots__/desktop-chrome-darwin/history-open.png # tests/visual/__snapshots__/desktop-chrome-darwin/intro.png # tests/visual/__snapshots__/desktop-chrome-darwin/lists.png # tests/visual/__snapshots__/desktop-chrome-darwin/notebook.png # tests/visual/__snapshots__/desktop-chrome-darwin/sidebar-open.png # tests/visual/__snapshots__/desktop-chrome-linux/features.png # tests/visual/__snapshots__/desktop-chrome-linux/history-open.png # tests/visual/__snapshots__/desktop-chrome-linux/intro.png # tests/visual/__snapshots__/desktop-chrome-linux/launch-open.png # tests/visual/__snapshots__/desktop-chrome-linux/lists.png # tests/visual/__snapshots__/desktop-chrome-linux/notebook.png # tests/visual/__snapshots__/desktop-chrome-linux/sidebar-open.png # tests/visual/__snapshots__/mobile-chrome-darwin/features.png # tests/visual/__snapshots__/mobile-chrome-darwin/history-open.png # tests/visual/__snapshots__/mobile-chrome-darwin/intro.png # tests/visual/__snapshots__/mobile-chrome-darwin/lists.png # tests/visual/__snapshots__/mobile-chrome-darwin/notebook.png # tests/visual/__snapshots__/mobile-chrome-linux/features.png # tests/visual/__snapshots__/mobile-chrome-linux/history-open.png # tests/visual/__snapshots__/mobile-chrome-linux/intro.png # tests/visual/__snapshots__/mobile-chrome-linux/lists.png # tests/visual/__snapshots__/mobile-chrome-linux/notebook.png # tests/visual/__snapshots__/mobile-chrome-linux/sidebar-open.png
…erif dependency Filed under [Unreleased] after merging main, so it sits above the frozen 2.4.0 section. Raised by Copilot on #166. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The branch's baselines predated #165's toolbar change and conflicted with the refresh it landed; these are re-recorded on the merged tree so both the serif headings and the icon search trigger are the baseline state. Linux baselines refreshed via /update-snapshots. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
/update-snapshots |
|
🎭 Refreshed visual baselines in 8d3ef56:
|
The update-snapshots workflow pushes with GITHUB_TOKEN, which leaves the CI run waiting for approval.
|
Thanks @DrDrij, this one is very carefully measured and the preview reads exactly like the Sphinx pages. I checked the static build directly: h1–h3 compute to As with #165 I pushed a few commits on top to get it mergeable after #165 landed; push back on any of them.
One idea for a follow-up rather than this PR: the first WebKit paint uses generic CI is green on the final head and Copilot's thread has a reply, so this is ready to merge from my side. |
Summary
Restores the heading typography of the Sphinx lecture builds (e.g. python-programming.quantecon.org), which this theme had left at the Tailwind typography plugin's defaults.
Heading face — the Sphinx builds set
h1,h2,h3in"PT Serif", serifand keep h4/h5 in the sans face; the same split now applies here viastyles/quantecon.css. The face is self-hosted through@fontsource/pt-serif(400/700, upright + italic), routed through the same Remix/esbuild pipeline as Source Sans 3 — no Google Fonts request. Family only: the plugin's heading weights resolve against PT Serif's 400+700 faces to the same 700 the Sphinx build renders (it asks for 900, which also resolves to 700).Heading scale — the Sphinx builds size content headings at h1
2emof the body, then h21.7rem/ h31.4rem/ h41.2rem/ h51remof an 18px desktop root. That root matches the 18px this theme sets on.article, so each value carries over as the same number inem, keeping the scale tied to the content size and the font-scale controls. h5 needs no rule — the plugin already leaves it at 1em.Line-height and h4/h5 weight — measured computed on the live Sphinx build, every heading level renders at line-height 1.15× its font-size, replacing the plugin's per-level values. h4/h5 also take the Sphinx build's weight 900: unlike h1–h3, where any heavy weight lands on PT Serif's single 700 face, the sans is a variable font with a real weight axis, and the plugin's 600/400 drew visibly lighter.
All of it is mirrored in
CRITICAL_CSSso the first WebKit paint on static-build navigations is already serif at the final sizes. The sizes matter there beyond reflow: the UA default for anh1inside<article>is 1.5em, so the page title would otherwise paint visibly small for a frame. Error pages get the face too — theirh1renders inside.article.Testing
myst startagainst the theme build andmyst build --htmlserved statically: computedfont-familyis"PT Serif", serifon h1–h3,document.fontsconfirms the 700 face loads, and no asset request fails (the built CSS references the fonts stylesheet-relatively).npm run compileclean.Known follow-up
/update-snapshotsonce this PR is open.