Case Study: How I Cut CLS from 0.35 to 0.02 — A Step-by-Step Breakdown

This is the post I’ve been building toward this whole series, honestly. Everything we’ve covered — images, fonts, ads, dynamic content, animations — actually showed up on one real project I worked on last year. I’m not going to name the client, but I will walk through exactly what I found and exactly what I did, in the order I actually did it.

The Starting Point

The site was a content-heavy publisher page — long articles, a handful of display ads, embedded videos, a newsletter signup bar, and a comment section at the bottom. Field data from Search Console showed a CLS of 0.35 on mobile, comfortably in “poor” territory. Desktop wasn’t much better at 0.28.

Step 1: Get a Baseline and Find the Biggest Offenders First

Before touching anything, I ran Lighthouse repeatedly and used Chrome DevTools’ Performance panel to record an actual page load, watching for “Layout Shift” events in the timeline. This is a step I’d genuinely recommend to anyone starting a similar cleanup — don’t guess where the problem is, watch it happen.

Three things jumped out immediately, roughly in order of impact:

  1. A display ad injected mid-article with no reserved space
  2. Hero and inline article images missing width/height attributes
  3. A newsletter signup bar that slid in from the top of the page after a 3-second delay

Step 2: Fix the Ad Slot First

This one alone accounted for the largest chunk of the score. I wrapped the ad slot in a fixed-height container matching the standard IAB medium rectangle size (300×250), with a neutral background color as a placeholder.

.ad-slot-inline {
  min-height: 250px;
  width: 300px;
  background: #f5f5f5;
}

Result after this single change: CLS dropped from 0.35 to roughly 0.19 on mobile. That’s more than half the total problem, from one fix.

Step 3: Add Dimensions to Every Image

Next, I went through the article template and made sure every <img> tag had explicit width and height attributes, plus aspect-ratio in the CSS as a responsive backup. This was tedious but mechanical — no clever decisions required, just discipline.

Result: down to approximately 0.11.

Step 4: Fix the Newsletter Bar

The newsletter signup bar was the trickiest one, actually, because it wasn’t obviously “wrong” — it just hadn’t been built with layout stability in mind at all. It was injected at the top of the document flow after a delay, pushing the entire article down.

I made two changes: switched it to position: fixed at the bottom of the screen instead of pushing content from the top, and reserved a small placeholder space for it from initial render, sized to match its actual height.

Result: down to approximately 0.04.

Step 5: Clean Up the Remaining Small Stuff

The last stretch was smaller wins — a font swap causing a minor reflow on headings (fixed with font-display: swap and matched fallback metrics), and a comment section that briefly collapsed to zero height before its widget script loaded (fixed with a min-height placeholder).

Final result: 0.02 on mobile.

The Full Breakdown

StepFix AppliedCLS After
Baseline0.35
1Reserved space for inline ad0.19
2Added image dimensions + aspect-ratio0.11
3Fixed newsletter bar positioning0.04
4Font-display + comment widget placeholder0.02

What I’d Tell Someone Starting This Same Process

If there’s one lesson I’d pull out of this whole exercise, it’s this: fix the biggest offender first, measure again immediately, and let the data tell you what’s next. I didn’t go in with a rigid plan to fix all five categories evenly — the ad slot alone was worth more than everything else combined, and finding that out early meant I spent my time where it actually mattered instead of spreading effort evenly across problems of wildly different sizes.

I’d also say: don’t chase 0.00. This project landed at 0.02, which is comfortably “good” territory, and further squeezing that number down would have meant diminishing returns for a lot more engineering effort. Perfect is rarely worth what it costs.

Wrapping Up This Series

That’s a wrap on this run of posts — from defining CLS all the way through to fixing a real page, step by step. If you’ve made it through the whole series, you’ve genuinely got everything I use myself on real projects. Go run Lighthouse on your own site, watch the Performance panel for a page load, and see what jumps out. I’d bet it’s one of the things we’ve covered somewhere in this blog.

By Dana Kowalski

I’m a front-end developer with 8 years of experience building and optimizing production websites — everything from small marketing sites to e-commerce platforms handling real traffic and real revenue.

Leave a Reply

Your email address will not be published. Required fields are marked *