Speed teardown · Core Web Vitals
332 KB PNG → 28 KB WebP: a before/after teardown (68 → 95+ PageSpeed)
This exact page scored 68 on Performance. Five fixes — images, fonts, JavaScript, CSS and preloads — took it past 95 with zero design change. Here are the weights, the code and the checklist.
The before/after in one table
| What | Before | After |
|---|---|---|
| Logo payload (header + hero) | 332 KB PNG served twice | 3.3 KB + 28 KB WebP |
| Font weights loaded | 6 weights, render-blocking stylesheet | 3 weights, async |
| Hero image hints | No size, no priority | width/height + fetchpriority + preload |
| Header cost | Translucent blur over content | Solid white, zero blur |
| JavaScript | mousemove tilt on document, layout reads | Menu + reveal only |
| PageSpeed Performance | 68 | 95+ |
Fix 1 — Right-size images, switch to WebP
The classic failure: one 500×500 PNG used for a 34-pixel header logo and a 300-pixel hero. The browser downloaded 332 KB to paint ~30 KB of pixels. The fix has three parts: generate each display size, serve WebP with a PNG fallback, and tell the browser the size up front so it can reserve space (no layout shift).
<picture>
<source type="image/webp"
srcset="/logo-420.webp 420w, /logo-500.webp 500w"
sizes="(max-width:600px) 64vw, 300px">
<img class="logo-hero" src="/logo-500.webp"
alt="PakWebDev circular logo" width="420" height="420"
fetchpriority="high" decoding="async">
</picture>
Results on this site: header logo 332 KB → 3.3 KB WebP, hero 332 KB → 28 KB WebP. The PNG fallback stays for ancient browsers but modern ones never touch it.
Fix 2 — Stop letting fonts block rendering
Google Fonts is fine; blocking Google Fonts is not. The old page loaded six weights (300–800) as a render-blocking stylesheet — the browser waited on a third party before painting text. Two changes: cut unused weights (body 300 → 400, keep 400/600/800) and load the stylesheet asynchronously:
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;600;800&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;600;800&display=swap"></noscript>
System fallbacks (system-ui, -apple-system, Segoe UI, Roboto) paint instantly; Inter Tight swaps in when ready. No invisible-text flash, no blocked first paint.
Fix 3 — Delete the cute JavaScript
The old page ran a mousemove listener on the whole document for a 3D logo tilt, plus per-row listeners doing layout reads. That is main-thread work on every pointer move — pure INP damage for decoration. We deleted it. What remains is a menu toggle and one IntersectionObserver reveal, both guarded:
- Progressive enhancement: hidden-until-reveal styles apply only under
html.js, so no-JS visitors and crawlers see full content. - Reduced motion respected: the reveal is skipped entirely under
prefers-reduced-motion. - Below-fold only: the hero is never hidden — LCP content paints immediately.
Fix 4 — CSS: solid header, cheaper paint
- Sticky white topbar, no blur.
backdrop-filter: blur(14px)forces the browser to repaint content behind the header on every scroll frame. Solid white + a 1px border paints once. - Cap the float animation. The logo float uses the compositor-only
translateproperty (no layout), and disables under reduced motion. content-visibility: autoon below-fold cards so the browser skips their layout until scrolled near.
Fix 5 — Preload what LCP needs, nothing else
<link rel="preload" as="image" href="/logo-420.webp" fetchpriority="high">
One preload for the LCP image. Everything else — OG images, manifest icons, footer links — loads lazily or on demand. Preloading more than the LCP asset just re-creates the congestion you removed.
How to measure (so your numbers are honest)
- Test the same URL in PageSpeed Insights, mobile, three runs, take the median.
- Record LCP, INP and CLS — not just the headline score.
- Re-test after each fix to attribute the gain; ship the winners together.
- Confirm in field data (CrUX) 28 days later — lab wins that don't survive real devices don't count.
Copy-paste checklist
- Serve each image at its display size; WebP + fallback;
width/heighton every<img>. fetchpriority="high"+ preload for the LCP image only.- Max 3 font weights; async font stylesheet; system fallback stack.
- No document-level mousemove; no layout reads in scroll/pointer handlers.
- No
backdrop-filteron sticky chrome; compositor-only animations. content-visibility: autobelow the fold; hero never hidden by JS.
FAQ
What was the single biggest speed win?
Images. Right-sized WebP cut ~300 KB from first load — more than fonts, JS and CSS combined.
Do Google Fonts hurt PageSpeed?
Only when render-blocking. Fewer weights plus an async stylesheet removes the block while keeping the design.
How should speed be measured?
PageSpeed Insights, same URL and throttling, three runs, median — tracking LCP, INP and CLS, then confirming with field data.
Want this teardown for your site?
Send your URL on LinkedIn — you get measured before/after numbers and a fixed scope. No forms, no queue.
