Lighthouse CI

The GitHub Actions build (.github/workflows/quarto.yml) runs Lighthouse against the freshly rendered www/ directory on every deploy, before the site is published. Budgets and audited pages live in lighthouserc.json.

  • Scores (median of 3 runs, mobile emulation) are written to the workflow run’s job summary; full HTML reports are attached as the lighthouse-reports artifact.
  • All assertions are currently warn-level so the build never blocks a deploy. Once the performance roadmap (planning/lighthouse-performance-prs.md) has landed and scores are stable, promote the ones you want enforced from "warn" to "error" in lighthouserc.json.

Running locally

Render the site, then run Lighthouse CI from the repo root (Chrome or Chromium must be installed; set CHROME_PATH if it isn’t auto-detected):

quarto render
npx --yes @lhci/cli@0.15.x autorun

Reports land in lighthouse-reports/ (gitignored). Open the .report.html files in a browser for the full audit detail, including the “Opportunities” list that drives the performance roadmap.

Baseline (2026-07-11, deployed build from the live branch)

Page Perf FCP LCP Notes
/index.html 64 4.7 s 7.0 s 13 render-blocking resources
/people.html 65 4.1 s 6.9 s
/fellowship.html 78 2.6 s 3.9 s CLS 0.16
/posts/…serratia/ 73 3.8 s 4.4 s CLS 0.107

After the PurgeCSS fix (512 KB → 165 KB Bootstrap bundle), the homepage measured perf 75, FCP 3.0 s, LCP 4.1 s in the same environment.

With PRs 2–3 also applied (parallel fonts.css + woff2 preloads, all head scripts deferred), the same environment measured:

Page Perf FCP LCP Notes
/index.html 78 2.1 s 4.1 s 0 render-blocking scripts
/people.html 67 3.2 s 6.9 s LCP is the people-listing images
/fellowship.html 83 1.5 s 3.5 s
/posts/…serratia/ 78 2.7 s 4.4 s

With the full roadmap applied (PRs 1–8: plus right-sized navbar logo, inline footer SVGs, responsive hero srcset, cache hardening), the homepage measured perf 81 (median of 3), FCP 2.1 s, LCP 3.7 s; fellowship 86 and the post page 82. Single runs in that environment vary by up to ~15 points under CPU contention — prefer the CI numbers (median of 3) and compare medians, not single runs.

Follow-up round: the people-page bottleneck turned out to be a render-blocking third-party ES6 polyfill (cdnjs) that Quarto’s listing template puts in

of the people/research/addiction pages, plus a deferred ~1 MB MathJax bundle no page needs. With both stripped post-render, the people page measured perf 87, FCP 1.8 s, LCP 2.9 s (median of 3).

CLS round: the ~0.1-0.16 CLS on every page was the announcement banner being revealed (and the fixed-header body padding being set) only at DOMContentLoaded. A pre-paint inline script (step 7 in _R/optimize_css_loading.R) settles both before first paint: fellowship CLS 0.155 -> 0, post 0.107 -> 0.044. A metrics-adjusted Arial fallback face for Nunito Sans further reduces font-swap reflow on real devices.

No significant known opportunities remain; watch the CI job summary for regressions.