perf: self-host KaTeX and jupyter-matplotlib stylesheets - #125
Open
DrDrij wants to merge 1 commit into
Open
Conversation
Both were render-blocking <link>s pointing at cdn.jsdelivr.net. The motivation is reach, not milliseconds. jsdelivr is intermittently unreachable from mainland China, which is a significant share of the QuantEcon readership. When it is blocked the maths markup still renders but arrives completely unstyled — fractions, radicals and matrices collapse into run-together text — so the lectures are at their least readable exactly where the maths matters most. Serving both from the site's own origin removes that dependency. Dropping a third origin from the critical path (DNS + TLS before first paint) is a secondary benefit. - KaTeX: imported from the `katex` package so Remix fingerprints it and emits the 60 referenced font files under the usual `/myst_assets_folder/_assets/` path, alongside every other bundled asset. Browsers still fetch only the few faces a page's glyphs need. This also retires the upstream `KatexCSS` export from @myst-theme/site, which pins 0.15.2 while this repo resolves 0.16.x. - jupyter-matplotlib: 316 bytes, vendored into the Tailwind bundle via styles/mpl-widget.css, so it costs no request at all. Kept rather than deleted — no current lecture renders ipympl output, but the theme is shared across lecture repos and any of them may enable it. `katex` becomes a direct dependency instead of a hoisted transitive one. The existing security override is rewritten as `"katex": "$katex"` so it defers to that direct version, preserving the >=0.16.21 floor from #104.
Contributor
|
Contributor
🎭 Visual regression resultsDetails
Skipped testsmobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both the KaTeX and jupyter-matplotlib stylesheets were render-blocking
<link>s pointing atcdn.jsdelivr.net. This serves them from the site's own origin instead.Why — reach, not milliseconds
jsdelivr is intermittently unreachable from mainland China, which is a significant share of the QuantEcon readership.
The failure mode matters here. When the CDN is blocked the maths markup still renders — MyST has already converted it at build time — but it arrives with no stylesheet. KaTeX depends entirely on CSS for layout, so fractions lose their bar and stack vertically, radicals lose their overbar, matrices collapse into run-together text, and superscripts sit inline. The lectures degrade worst exactly where the maths matters most, and they do so silently: nothing errors, the page just becomes wrong.
Serving from the site's own origin removes that dependency. Dropping a third origin from the critical path (a DNS lookup and TLS handshake before first paint) is a secondary benefit.
What changed
KaTeX.
katexis already present in the tree, so this adds no new package — it becomes a direct dependency instead of a hoisted transitive one ofmyst-transforms/mermaid. A localapp/links.tsreplaces theKatexCSSexport from@myst-theme/site, which is the thing that pointed at the CDN. The two routes that consumed it now import from~/links; no upstream patch is needed.Remix fingerprints the import and emits the stylesheet plus its 60 referenced font files into
public/build/_assets/, served at/myst_assets_folder/— the samepublicPathevery other bundled asset already uses, so no new path handling is introduced. Verified against a realremix build: all 60 fonts emitted and everyurl()correctly rewritten. Browsers still fetch only the handful of faces a page's glyphs actually need, not all twenty.This also retires a version skew: the upstream export pins KaTeX 0.15.2 while this repo resolves 0.16.x. Worth being precise — that skew is not currently causing breakage. The two stylesheets are functionally equivalent for the pages checked (248 vs 250 rules, identical nine
@font-facefamilies), so this is hygiene rather than a bug fix.jupyter-matplotlib. 316 bytes, vendored verbatim into the Tailwind bundle as
styles/mpl-widget.cssand pulled in bystyles/app.css, so it now costs no request at all. It is kept rather than deleted: no current lecture renders ipympl output, but this theme is shared across lecture repos and any of them may enable%matplotlib widget. The one!importantin that file is upstream's, preserved deliberately rather than silently "improved".Removing only one of the two would have achieved little, since both sat on the same jsdelivr origin — the DNS and TLS cost is per origin, not per file.
The dependency change worth a second look
npm rejects an override that conflicts with a direct dependency of the same name, and
katexalready had a security override from #104. The fix is npm's own idiom:$katexmakes the override defer to the direct version, so the>=0.16.21floor still applies to every transitive consumer. Lockfile churn is a single line.Related, not included
styles/app.cssstill opens with an@importtofonts.googleapis.comfor Source Sans 3. That is the same class of problem and arguably a larger one — Google Fonts is blocked in mainland China, it affects body text on every page rather than just maths, and as a CSS@importit is discovered only afterapp.cssparses, forming a serial request chain that cannot be preloaded.Deliberately left out of this PR, and it is a larger change than a one-line swap. Scoping notes for whoever picks it up:
url()inside an@imported stylesheet (verified experimentally), so@import '@fontsource-variable/source-sans-3'instyles/app.csswould emit font paths relative to the output file and 404. It needs the Remix import route this PR uses for KaTeX, where esbuild rewrites the URLs and emits the files.@fontsource-variable/source-sans-3declares the family asSource Sans 3 Variable, sotailwind.config.jsand the inlined critical CSS inapp/root.tsxboth need the name updated — otherwise the first paint falls back to system sans and the FOUC returns.Because it touches
app/root.tsx, it will conflict with this PR and with #123, so it is best done after both land.Note for merge order
This and the Font Awesome removal (#124) both delete adjacent entries from the same
links()array, so whichever merges second will show a trivial conflict. The resolution is to keep both explanatory comments and drop both<link>entries.