I'm Cody Wang. I build and optimise Prismic, SvelteKit and Next.js sites, mostly for clients in New Zealand and for Chinese brands going international.
These six things are ones I've fixed myself, more than once. No theory dump. Just what it looked like, what I assumed at first, what I actually changed, and where the official docs are. Images: alt text, dimensions, file size
The most common one is a missing alt. People either write alt="image" or stuff keywords in there. Both read badly through a screen reader. Alt text describes the picture to someone who can't see it: alt="Dark grey steel garage door after installation, seen from the street". Purely decorative images get alt="" — an empty string, not an omitted attribute.
If editors upload content through a CMS, the template has to cover for them: alt={image.alt || contextFallback}. Don't assume everyone remembers. On one client site I counted 32 images without alt; after the fix, zero.
Second issue is dimensions. Without width and height, the browser doesn't know how much room an image needs. It lays out everything else first, then reflows once the image arrives, and the user watches the content jump. That's where CLS comes from.
Third is file size, and it's the most visible one. A hero exported straight from the design file can land at 527 KB. I once found a star rating icon, an SVG, weighing 123 KB when it should have been about 2 KB.
I do three things together: convert to WebP or AVIF, serve the size the layout actually uses (a 400px card doesn't need a 2000px image), and let the CDN handle it with something like ?auto=format,compress&q=75&w=800. Also don't lazy-load the hero — give it fetchpriority="high" and lazy-load the rest.
Docs: https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images Don't load the whole font family
Usually the entire family is being pulled down at once: four to six weights, plus full character sets. While it downloads, the browser may show nothing at all. Chinese sites hit this hard, since a single CJK font can run into megabytes.
What I do: keep only the weights in use, say 400 and 600; ship woff2 split by unicode-range, so an English page never fetches the CJK file; and set font-display: swap so text appears in the system font first.
On one site that was Roboto at 468 KB plus Raleway at 309 KB, 777 KB total, and after the split 57 KB plus 43.7 KB — 100.7 KB.
Docs: https://developer.chrome.com/docs/lighthouse/performance/font-display Third-party scripts: not whether, but when
Scrolling feels sticky, clicking a button does nothing for a moment. That's usually total blocking time.
GTM, the Facebook pixel, chat widgets — they all load as synchronous JavaScript and compete with your page for the main thread. While it's busy, taps do nothing.
Deleting them is cleanest, but on most client work the tracking scripts are a business requirement and I can't touch them. So I change when they load instead:
Inject them once the page is idle, or after the first sign of interaction. Nothing is removed and nothing stops reporting. With that change on one site, TBT went from 284 ms to 179 ms.
Worth knowing before you promise a number: changing when a script loads is something you can usually get approved. Removing it is not. Where the tracking scripts stay, mobile performance has a ceiling, and it's not a ceiling you can code your way past.
Docs: https://developer.chrome.com/docs/lighthouse/performance/third-party-summary and https://web.dev/articles/tbt Navigation built from buttons is invisible to crawlers
