Performance check plan
Goal: measure current page-load performance on the v2 site, fix what’s cheap, and confirm we’re shipping a fast, professional-feeling site. Includes a separate mobile visualization pass for polish (audience is primarily desktop, but mobile presentation matters for credibility).
Context for execution
- Plan status: drafted 2026-05-21. Pre-launch phases (1–4 below) run on a local production build or Netlify drag-and-drop preview before cut-over. Phase 5 is post-launch.
- Hosting: GitHub Pages, custom domain
owaspsamm.org. Limited control over headers, no edge Brotli, no per-route cache rules. Performance wins must come from what is delivered — image weight, JS payload, font strategy — not from infrastructure. - Third-party scripts: Google Analytics (
G-44N5RHDT94) and Scarf pixel are consent-gated and default-denied. They don’t fire until a user accepts cookies. The consent banner itself runs on every page and is in the critical path — measure with consent both accepted and declined. - Build command for measurements:
hugo --minify --environment production. Same command CI uses. Don’t measure againsthugo server— its livereload script and unminified output will skew everything. - Hugo image processing: Hugo’s
resources.Imagepipeline (.Resize,.Process, format conversion to WebP/AVIF,srcsetgeneration) is available. Whether it’s used today is part of the audit.
Invocation
When ready to run this plan, open this page and say “run the performance plan”. I’ll start with phase 1 (baseline measurements) and stop for confirmation before applying any fixes.
Pages to test
Picked to match real traffic (per GA) plus shape coverage of the site.
Tier 1 — high traffic, must be fast:
/— homepage/model/— model overview/model/governance/strategy-and-metrics/— practice page (representative deep model page)/model/governance/strategy-and-metrics/stream-a/— stream page (includes the stream-guidance partial, the heaviest model template)
Tier 2 — secondary traffic per GA:
/assessment/— assessment landing/docs/getting-started/— quick start guide
Tier 3 — shape coverage:
/blog/— blog index (cards + thumbnails)/blog/<a-recent-post>/— a representative blog post with images/user-day/2025dc/— user-day page (lots of session cards, image-heavy)
Phases
Phase 1 — Baseline (~1h)
Run Lighthouse on each tier-1 and tier-2 page. Throttling profile: Slow 4G + 4× CPU (standard mobile preset, also fine as a pessimistic desktop proxy).
Capture for each page:
- Lighthouse scores (Performance, Accessibility, Best Practices, SEO)
- Core Web Vitals: LCP, INP, CLS
- TTFB
- Total transferred bytes
- Number of requests
Also note any Lighthouse opportunities it surfaces (unused CSS, unoptimized images, render-blocking resources). These feed phase 2.
Capture twice per page: with cookie consent accepted and declined. The delta is the cost of GA + Scarf.
Phase 2 — Asset audit (~2h)
Per tier-1 page:
- Images: list largest by transferred bytes. Note format (png/jpg/webp/avif), intrinsic dimensions vs. rendered dimensions, presence of
loading="lazy", presence ofwidth/heightattributes (for CLS). - CSS: total bundle size. Use Chrome DevTools → Coverage → record a page load + interaction → percentage of CSS unused. Identify the biggest unused blocks.
- JS: what’s loaded, when, deferred or not. Check the cookie-consent banner script in particular — it’s the only universal JS payload.
- Fonts: count, format (woff2 ideally), loading strategy (
font-display), whether any are preloaded, whether any are loaded but unused. - Build-time view: run
hugo --templateMetrics --templateMetricsHintsto surface slow templates. Not a runtime concern but useful if Hugo build time is climbing.
Output of phase 2: a short list of fixes ranked by expected impact ÷ effort.
Phase 3 — Quick-win fixes (variable)
Likely candidates (apply what’s cheap, defer what isn’t):
- Image optimization via Hugo’s
resources.Imagepipeline: resize source images to the displayed dimensions, output WebP/AVIF with JPG fallback, emitsrcsetfor responsive sizing. - Lazy loading: add
loading="lazy"to below-fold<img>tags. - Fix CLS by adding
width/heightattributes everywhere images don’t currently have them. - Font loading: ensure
font-display: swap, preload the critical font(s), remove any unused weights/styles. - CSS pruning: drop unused selectors found in phase 2. Consider splitting layout-specific CSS so it loads only where needed.
- Defer non-critical JS: anything that doesn’t need to run before paint should be
defer/asyncor moved to end-of-body. - Cookie-consent banner: confirm it doesn’t block render unnecessarily. If it does, consider inlining a minimal CSS skeleton.
Hard / out of scope without strong justification:
- Critical CSS inlining (high complexity for marginal wins on a static site).
- Service workers (overkill for the audience and adds debug surface).
- Switching off GitHub Pages.
Phase 4 — Re-measure & validate (~1h)
Re-run Lighthouse on the same pages, same conditions, same time of day if possible. Document deltas. If a target is missed, decide: fix more, or accept and document why.
Targets:
- Lighthouse Performance ≥ 90 on all tier-1 and tier-2 pages (desktop preset).
- Lighthouse Performance ≥ 75 on the same pages with mobile preset (Slow 4G + 4× CPU).
- LCP < 2.5s on tier-1.
- INP < 200ms on tier-1.
- CLS < 0.1 on all tested pages.
- Total transferred per page < 500 KB (excluding any user-initiated PDF downloads).
Phase 5 — Lighthouse CI (post-launch)
Once the layer-1 link-check Action is in place and stable, add a separate GitHub Action that runs Lighthouse CI against built pages on PRs. Set perf budgets matching the targets above; fail the build if a budget regresses by more than a tolerance (e.g. -5 points or +10% transferred bytes).
Don’t combine this with the link-check Action — keep them as separate workflow files so a failure in one is diagnosable without reading through the other.
Mobile visualization pass (parallel track)
Audience is mostly desktop, so this is not the primary perf optimization. It’s a polish / professionalism check: the site should look clean and feel right on a phone for credibility reasons (people will share links from Slack, LinkedIn, etc., and open them on mobile).
Run alongside phase 2, separate from perf:
- Open the tier-1 + tier-2 pages on a real phone (or DevTools device emulation at iPhone 14 / Pixel 7 sizes).
- Check: nothing overflows horizontally, tap targets are ≥ 44 px, line lengths are readable, images don’t squash to unrecognisable sizes, the model navigation sidebar is usable, the assessment table doesn’t break.
- Cross-reference with the Mobile link audit roadmap item — that work is about hover-only link affordances, which is part of this same pass.
- Output: a short list of mobile-specific fixes folded into phase 3 alongside the perf work.
Open questions
- Which specific blog post should be in tier 3? Pick one with images that’s representative of how blog posts actually look.
- Acceptable target deltas: are the suggested targets above tight enough? Too tight?
- Does the team want to set a transferred-bytes budget per template family (e.g. all model pages must be under X KB), or just per-page in phase 4?
Out of scope
- Server-side / CDN-level optimisations (we’re on GitHub Pages).
- Backend / database performance (no backend).
- Accessibility audit beyond Lighthouse’s a11y score — that’s a separate review.
- SEO beyond Lighthouse’s SEO score — also separate.