Skip to content

perf: scope KaTeX/fonts/middleware/JSON-LD, fix stale caching + O(N^2) build#21

Merged
rNLKJA merged 2 commits into
v5from
perf/loading-speed
Jul 18, 2026
Merged

perf: scope KaTeX/fonts/middleware/JSON-LD, fix stale caching + O(N^2) build#21
rNLKJA merged 2 commits into
v5from
perf/loading-speed

Conversation

@rNLKJA

@rNLKJA rNLKJA commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

Ten independent load-time / build-time fixes from a full performance audit. Each verified via clean npm run build + npm run lint (0 errors).

Real load-time wins:

  • KaTeX JS+CSS scoped to the 39/68 /knowledge pages that actually render maths (was leaking a 76KB gzip chunk into all 68). Also switched to katex-swap.min.css to remove a FOIT risk.
  • Bitcount local font no longer eagerly preloaded (~35KB) except where it's actually above the fold.
  • middleware.js matcher tightened to skip static file extensions — every font/image/css request no longer takes an Edge Middleware hop.
  • Homepage-only JSON-LD (breadcrumb, projects, credentials) moved out of global _document.jsx into pages/index.jsx — was shipping on all ~300 pages.

Correctness/hygiene fixes found along the way:

  • sw.js: /images/* switched from cache-first to stale-while-revalidate (filenames aren't content-hashed, so a replaced image could stay stale forever for returning visitors).
  • Deleted 2 unreferenced images (2.3MB, zero references anywhere in the repo).
  • Fixed a CSS bug where h1's font-family didn't match the compiled @font-face name — ~70 page titles were silently falling back to system monospace.
  • Removed dead dns-prefetch hints + remotePatterns entries for hosts no longer used (logos are self-hosted now).
  • lib/posts.js: split metadata-only parsing from full-content parsing, turning /blog/[slug]'s O(N²) build-time markdown parsing into O(N). Measured 11.3s → 3.4s for the current 18 posts (build-time only, not visitor-facing).

Deliberately NOT done (assessed and scoped back after finding real risk):

  • Splitting both locale JSON files apart — real gzip cost is ~28KB once per session (not per-page, thanks to existing immutable caching), and a correct fix needs getStaticProps rolled out across ~300 pages.
  • Splitting DM Sans/Playfair's italic preload — next/font's preload option is all-or-nothing per call; a safe split needs a second font-family + CSS routing rules, too much surface for a site with zero test coverage.

Test plan

  • npm run lint — 0 errors, 194 pre-existing warnings (unchanged)
  • npm run build — succeeds, same pre-existing warnings (unrelated @vercel/kv module notice)
  • Verified via build manifest: katex chunk present on exactly 39/68 knowledge pages (was 68/68)
  • Verified all 7 homepage JSON-LD blocks parse as valid JSON; confirmed breadcrumb/projects/credentials schemas absent from non-homepage pages
  • Verified middleware matcher regex against sample paths (fonts/images/css/sw.js/robots.txt excluded; real page routes still matched)
  • Verified Bitcount CSS variable compiles correctly in output CSS
  • Live browser/visual QA not done — the preview tooling was unavailable for this session. Please smoke-test locally before merging: homepage, a knowledge page with maths (e.g. /knowledge/kalman-filter) and one without (e.g. /knowledge/topic-modelling), locale switch, dark/light mode, and the CLI easter egg (curl the site or check /api/curl still triggers for CLI user agents).

🤖 Generated with Claude Code

…) build

Ten independent load-time and build-time fixes, each verified via a clean
production build + zero-error lint pass:

- Split Formula/TeX out of KnowledgeLayout.jsx into KatexFormula.jsx so the
  katex JS library (76KB gzip) and its CSS only load on the 39/68 knowledge
  pages that actually render maths, not all 68. Switched katex.min.css to
  the -swap variant (font-display:swap) to remove a FOIT risk on those pages.
- lib/fonts.js: stop eagerly preloading the Bitcount local font (~35KB)
  everywhere except where it's actually above the fold; display:swap already
  prevents FOIT elsewhere.
- middleware.js: tighten the matcher to skip static file extensions so every
  font/image/css request doesn't take an Edge Middleware hop.
- Move homepage-only JSON-LD (breadcrumb, projects, credentials) out of the
  global _document.jsx into pages/index.jsx — was shipping on all ~300 pages.
- Remove dead dns-prefetch hints and images.remotePatterns entries for hosts
  no longer used now that logos are self-hosted.
- public/sw.js: switch /images/* from cache-first to stale-while-revalidate
  — filenames aren't content-hashed, so cache-first could serve a replaced
  image forever to returning visitors.
- Delete two unreferenced images (heroSection1.png, 404.png — 2.3MB, zero
  references anywhere in the repo).
- Fix a CSS bug where h1's font-family targeted a string that doesn't match
  the compiled @font-face name, silently falling back to system monospace
  on ~70 page titles site-wide.
- lib/posts.js: split parsePost into a cheap metadata-only parse (used by
  getAllPosts) and the full remark pipeline (used per-page) — turns
  /blog/[slug]'s O(N^2) build-time parsing into O(N). Measured 11.3s -> 3.4s
  for the current 18 posts.

Deliberately NOT done: splitting both locale JSON files apart (the real
gzip cost is ~28KB one-time per session, not per-page, and a correct fix
needs getStaticProps rolled out across ~300 pages) and splitting DM
Sans/Playfair's italic preload (next/font's preload option is all-or-nothing
per call, so a safe split would need a second font-family and CSS rules to
route italic text to it — too much surface for a zero-test-coverage site).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rin-contact Canceled Canceled Jul 18, 2026 2:56am

Request Review

CI's format check was failing on v5 itself (unrelated to the perf changes
in this branch) — fixing here so this PR's CI is green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@rNLKJA
rNLKJA merged commit 2ca1155 into v5 Jul 18, 2026
4 of 5 checks passed
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.

1 participant