“Your Core Web Vitals are poor” is hard to act on. Three metric names, three different units, three different thresholds.
This article explains which moment of a page’s life each metric (LCP, INP, CLS) measures. We also point curl at a real page on this blog and read what the HTML alone can tell us: which files it loads, whether images declare their dimensions, and so on.
Note: The thresholds below are the ones published on Google’s web.dev. The measurements here are limited to what we could read from the HTML and response headers from one machine. We did not measure what real visitors experience (field data), so this is not a pass/fail verdict on this blog.
Core Web Vitals split the experience into three aspects
| Metric | What it looks at | “Good” | “Poor” |
|---|---|---|---|
| LCP (Largest Contentful Paint) | When the main content appears | 2.5 s or less | over 4 s |
| INP (Interaction to Next Paint) | How quickly the page responds to input | 200 ms or less | over 500 ms |
| CLS (Cumulative Layout Shift) | Whether things move unexpectedly | 0.1 or less | over 0.25 |
Assessment uses the 75th percentile of page visits: “do three out of four visitors get this value or better?” It is not an average, so visitors on older devices or weak connections count.
INP replaced FID (First Input Delay) in March 2024, which is why older articles still mention FID.
LCP: when the largest element is painted
LCP is the time from navigation start until the largest element in the viewport is rendered. Candidates include images, video poster images, elements with background images, and blocks of text. The largest element can change as the page loads. A heading may be largest at first, then a big image arrives and takes over. For a page with a hero image, the LCP element is that image; for a text-centered page, it is usually the first block of text or a heading.
Think of LCP as four phases
- Server response (TTFB): until the first byte of HTML arrives
- Discovery: until the browser finds the LCP resource’s URL
- Download: until the file is fetched
- Rendering: until it is painted
Making an image smaller does not help if the real delay is phase 2. If an image URL exists only inside CSS or JavaScript, the browser cannot know about it until those are loaded and run. An <img> in the HTML can be found early by the preload scanner.
Do not lazy-load the LCP image
loading="lazy" postpones loading until an image nears the viewport. That is right for images far down the page, but on the large image in the first screen it delays your own LCP. Blanket “lazy on every image” rules are a common cause. WordPress core adds loading="lazy" automatically but includes adjustments to skip images likely to be above the fold; a theme that outputs images itself may bypass them. Check the HTML source of your own first image.
INP: from an interaction to the next paint
INP measures the time from a click, tap, or key press until the next frame is painted. Across all interactions during the visit, the slowest one is used (for pages with many interactions, outliers are discounted). Unlike LCP, it covers the entire time the page is open. A page can have a good LCP and still feel stuck when a button is pressed.
One interaction has three parts:
- Input delay: the browser is busy and cannot start responding yet
- Processing time: running the event handlers (JavaScript)
- Presentation delay: from the end of processing until the screen actually updates
While JavaScript runs, the browser’s main thread cannot paint or handle the next input. A poor INP is therefore usually about heavy JavaScript occupying the main thread. On WordPress sites, the more plugin and theme scripts you load, the more likely this becomes.
CLS: how much the layout moved
CLS is the accumulated amount of unexpected movement while the page is displayed: reading text when an ad is inserted and pushes the paragraph down, or tapping a link just as the layout shifts and hitting a different one. The score combines how large the moved area was and how far it moved; it has no unit. 0.1 or less is “good”.
Common causes:
- Images without
width/height: laid out with no height, then push content down once loaded - Late-inserted elements: ads, embeds, banners
- Web font swaps: a fallback font and the final font have different widths, so lines re-wrap
- Not counted: shifts shortly after a user interaction (within about 500 ms) are treated as expected
For images, adding width and height attributes is enough: modern browsers derive the aspect ratio from them and reserve the box before loading, even when CSS makes the width flexible.
Reading this blog’s HTML
We fetched an article page with curl:
curl -s -D headers.txt -o page.html \
-w "ttfb=%{time_starttransfer} total=%{time_total} size=%{size_download}\n" \
https://wpmm.jp/blog/<article-url>/
Key points (6 Oct 2026, a single run; it varies with line and time of day, so treat it as a reference):
HTTP/2 200; HTML body about 57 KB uncompressed- TTFB about 0.15 s; whole HTML fetched in about 0.17 s
- The only
<img>elements are two header logos, and both declarewidthandheight(a CLS-safe pattern) - This page has no images in the body, so the LCP candidate would be a heading or a text block
- Five stylesheets are loaded: three from external CDNs (Google Fonts, an icon font, a syntax highlighter theme), one from the theme itself
The last point matters for LCP. Stylesheets block rendering until they load, so more external stylesheets mean the first paint depends on more external servers. A web font with display=swap does not wait for the font; it shows text in a fallback font first. That avoids invisible text, at the cost of a possible layout shift when the font swaps in.
So reading HTML shows what the page waits for and whether image boxes are reserved. But the actual LCP, INP and CLS values depend on rendering and real interaction, and cannot be settled from HTML.
Lab data vs. field data
| Kind | What it is | Typical tools |
|---|---|---|
| Lab data | Measured under fixed conditions on your machine or in automation | Lighthouse, browser DevTools |
| Field data | Measured in real visitors’ browsers | Chrome User Experience Report (CrUX), Search Console’s Core Web Vitals report |
Assessment rests on field data. Lab data suits comparing before and after under identical conditions. INP needs real interactions and is hard to capture in the lab, where Total Blocking Time (TBT) is often used as a proxy. Low-traffic pages may lack enough field data to show at all; missing numbers are not necessarily a fault.
What to check before optimizing
- Find which metric is poor, using field data
- Find which pages; the whole site is rarely uniformly bad
- LCP: identify the LCP element and which of the four phases is long
- INP: identify the slow interaction and the script occupying the main thread
- CLS: identify which element moves (a DevTools performance recording shows it)
- Change one thing at a time and re-measure under the same conditions
Image-related LCP work is covered in “WebP and image optimization basics“, server-side compression in “gzip/Brotli basics“, and caching behind TTFB in “Cache-Control vs. ETag“.
Summary
- Core Web Vitals measure loading (LCP), responsiveness (INP) and visual stability (CLS)
- Assessment uses 75th-percentile field data; lab data suits before/after comparison
- Split LCP into four phases to see what to fix; never lazy-load the first-screen image
- INP covers every interaction while the page is open; heavy main-thread JavaScript is the usual cause
- CLS comes from images without dimensions, late insertions and font swaps
- HTML shows what a page waits for, but real values only come from measurement
Starting from “which moment, for whom” makes it clearer what to check next.