Performance

Core Web Vitals for Content Sites, Decided Before Launch

Nearly every article about Core Web Vitals is written for someone who already has a problem. Layout is jumping, the score is red, something regressed. Fixing is the expensive way to do this.

The cheap way is to make six decisions at build time and then stop thinking about it. Five of them cost nothing. The sixth costs a little white space, and it is the one that matters most.

1. Do not load a web font

Web fonts are the single easiest way to buy yourself a layout shift.

The browser finds text in a font it has not downloaded yet. It has two options: block rendering until the font arrives, or paint with a fallback and swap when the real one shows up. Blocking hurts Largest Contentful Paint. Swapping causes a visible reflow, because the fallback and the web font rarely have identical metrics.

There are ways to soften this — preload the font file, or use size-adjust in an @font-face to make the fallback metrics match. Both work. Both are also ongoing maintenance for a benefit most readers will never consciously notice.

A system font stack has none of these problems. It renders immediately, costs no requests, and cannot shift:

font-family: system-ui, -apple-system, "Segoe UI", Roboto,
  "Helvetica Neue", Arial, sans-serif;

If a brand needs a specific typeface, use it in the logo and the headings as an image or an SVG, and keep body text on the system stack. You get the identity without paying for it on every page.

2. Keep CSS on the critical path

For a site built from templates, the stylesheet is small — a few kilobytes. Inlining it into the document removes a blocking request on every page load.

Most build tools can decide this automatically. In Astro, a single config line does it:

build: { inlineStylesheets: 'auto' }

For a larger stylesheet, that strategy changes: ship a small critical sheet inline, and defer the rest. But a content site’s stylesheet is not usually large enough for that to be worth the complexity.

3. Do not ship JavaScript you cannot justify

Every script is a download, a parse, and a main-thread task competing with the browser’s attempt to paint.

A content site needs very little. Navigation is links. A mobile menu can be a checkbox and a sibling selector. Anything that genuinely requires JavaScript — search, comments — can load after the page is interactive, not before.

The discipline is simple: no script goes in the document unless you can name the reader-visible feature it delivers. “The framework includes it” is not that.

4. Always declare image dimensions

This is the most common CLS cause and the easiest to fix.

An <img> with no width and height takes up zero height until the file arrives. Then it expands, and everything below it moves. The browser reserves space only if you tell it the aspect ratio:

<img src="/photo.jpg" width="1200" height="800" alt="Description">

Modern browsers derive the aspect ratio from those attributes before the image loads, so the space is reserved up front. If you need the image to be fluid, add max-width: 100%; height: auto in CSS — the attributes still do their job.

5. Reserve space for ads before you have any

This is the decision that is genuinely hard to reverse.

Ad units load asynchronously. A slot that had no height when the page painted and then expands to 250 pixels pushes everything beneath it downward — sometimes more than once, as the ad server responds and an ad renders into the slot. On a content site with an ad below the first screen of text, this is the largest single source of layout shift you will ever introduce.

The fix is to reserve the space in CSS, in the markup, from day one:

.ad-slot {
  min-height: 250px;      /* matches the unit you will serve */
  contain: layout;         /* keeps the shift inside the container */
  background: transparent;
}

The cost is honest and unavoidable: before you are running ads, that space is empty. Some visitors will see a gap. That is the trade — a gap that no one measures, instead of a layout shift that Google measures on every pageview.

Two follow-ups worth internalising:

  • Serve one size, not responsive. A slot that changes size between breakpoints will shift when the viewport changes. Pick a size per breakpoint and keep it fixed there.
  • Never stack a second unit above the first screen of content. The lower a shift happens, the less it hurts — but a shift near the top can move your LCP element itself.

In this site’s own templates, the ad container ships with an explicit height and a switch that decides whether it renders at all. The height is present in the CSS before the first ad network is approved, because adding it later means revisiting every article that has already been published.

6. Cache aggressively, and correctly

Fingerprinted assets — a stylesheet with the hash in its filename — can be cached forever. The filename changes when the content does, so a long max-age is safe:

/css/*.abc123.css    Cache-Control: public, max-age=31536000, immutable

HTML should not be cached that way. It needs to reflect the newest deploy, so a short or revalidating policy is right for it.

Measure the right number

One last thing that trips people up: lab data and field data are not the same thing, and only one of them is your score.

Tools like PageSpeed Insights run a synthetic test from a controlled environment. That is useful for finding problems, but the number Google uses for ranking comes from the Chrome User Experience Report — real visitors, on real devices, on real connections, over a 28-day window.

A site can score 100 in a lab and fail in the field, because the field includes the phone on a slow connection with a browser full of extensions. When the two disagree, the field number is the one that counts.

Why deciding early is the whole point

None of these decisions is difficult. Each of them is significantly harder to make after fifty articles are published, because some of them — reserved ad space, in particular — touch every page that already exists.

Decide them once, at build time, and Core Web Vitals stops being a project you have to run.


Written by TestedHost. Every recommendation on this site comes from running the setup described, on a live deployment — not from a vendor spec sheet. Spotted something out of date? Tell us.