One green Lighthouse score on your home page tells you almost nothing about the pages that actually close deals. Core Web Vitals are a per-page-type budget, not a sitewide grade.
The three metrics — Largest Contentful Paint, Interaction to Next Paint, Cumulative Layout Shift — share one set of targets, but how you hit them differs by page type, because the largest element on the page is a hero image on the home page and a paragraph of body copy on a blog post. This spoke maps the LCP candidate and tuning per page type, the INP regressions to avoid, the structural CLS conventions, and how to watch real-user field data — all against the budgets the AI-native stack commits to: LCP under 1.5s on mid-tier mobile (tighter than Google's 2.5s "good" line), INP under 200ms, CLS under 0.1.
Why per-page-type tuning matters
The parent pillar, AI-Native Website in 4 Weeks, sets the sub-1.5s LCP target; this spoke is how you actually hold it across eleven different page types. Three reasons it has to be per-type:
The LCP candidate differs by page type. A home page's is the hero image; a blog detail's is the H1 plus the first paragraph; a case study's is the cover image. Tuning to one canonical candidate fails every page where the candidate is something else.
Conversion-page speed is a revenue issue, not just an SEO one. Service-detail and contact pages convert, so their speed affects bounce at the exact moment of decision. Google's own page-experience research links faster LCP to measurable conversion-rate lift on commerce-shape pages.
Answer-engine cite-rate correlates with field-data CWV at the margin. Bad CWV rarely tanks organic rank enough for an operator to notice, but the cite-rate from Perplexity and Copilot does track field-data vitals. Page-type-tuned CWV is one of several signals that compound into citation density.
LCP — candidates and tuning per page type
LCP measures the time to render the largest above-the-fold element. The candidate, and the fix, per page type:
| Page type | Likely LCP candidate | Tuning strategy |
|---|---|---|
| home | Hero image (full-width, above fold) | fetchpriority="high", no lazy-load, AVIF + WebP fallback, served from edge |
| services_hub | H1 + ItemList | Inline critical CSS for the H1; defer card images below the fold |
| service_detail | H1 + hero illustration | Same as home; the H1 carries more visual weight |
| pricing | H1 + pricing table | Critical CSS includes the table layout; the numbers must not shift |
| case_study_detail | Cover image + client-name H1 | Cover image gets fetchpriority="high"; client logo inline as SVG |
| about | H1 + headshot or hero | Same as home |
| blog_hub | H1 + featured-post card | Featured-post image gets fetchpriority="high" |
| blog_detail | H1 + first paragraph of body copy | Usually no hero; the LCP is typography, so inline the critical font + CSS |
| pillar | Same as blog_detail | Long-form copy; LCP is typography-driven, not image-driven |
| contact | H1 + form layout | Critical CSS includes the form; no third-party form embeds |
Five tuning patterns recur across the table: fetchpriority="high" on the LCP image so the browser prioritizes it; AVIF with a WebP fallback via <picture>; inline critical CSS for above-the-fold typography (Astro's inlineStylesheets: 'auto' handles it — verify it is on); no third-party scripts on the critical path (defer analytics, A/B, and consent banners past load); and edge delivery so the LCP image serves from the Cloudflare edge nearest the visitor, not a long-haul origin.
INP — regressions to avoid
INP replaced FID in March 2024 and measures the longest interaction latency across the page's life; 200ms or under is "good." Five regressions recur on a static-first stack:
- Oversized hydration islands. Hydrating a 500KB bundle for a single form produces 300ms+ INP at submit. Split client JS per island; lazy-hydrate below-fold islands with
client:visible. - Heavy form-submit handlers. Validation plus an analytics fire plus a fetch can block the main thread. Defer the analytics fire until after the submit resolves; push validation to
requestIdleCallback. - Third-party scripts on the critical path. Chat widgets and heatmap tools are common culprits. Load them deferred and, where possible, gate them behind a user interaction.
- CSS-in-JS runtime overhead. Tailwind's pre-compiled CSS avoids it; migrations off styled-components/Emotion routinely shave INP on interaction-heavy pages.
- Virtualized lists with poor scroll handlers. Prefer static rendering for lists under ~100 items; reserve virtualization for the genuinely large ones.
Want a CWV-tuned site that holds sub-1.5s LCP at field scale? Talk to the team. →
CLS — prevention is structural
CLS measures visual stability; 0.1 or under is "good." Regressions are almost always preventable at authoring time, not tuned away later. Five conventions prevent it at the source:
- Explicit width and height on every
<img>(or anaspect-ratio) so the browser reserves the space before the image loads. font-display: swapon every@font-faceso text paints immediately instead of after a blocking font fetch.- Reserved heights for ad slots, embeds, and async content — a
min-heighton the container stops the shift when the content arrives. - No late-loading hero above the fold. If an illustration is large enough to tempt lazy-loading, compress it instead and load it at first paint.
- No CSS-in-JS hydration shifts. Pre-compiled Tailwind eliminates the re-layout that runtime CSS-in-JS can trigger on hydration.
Field data — CrUX + Lighthouse CI
Lab data answers "did this build regress?"; field data answers "are real users hitting good vitals?" You need both. Lighthouse CI runs the lab test on every deploy and gates it:
# .github/workflows/lighthouse.yml
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
https://staging.nobrainermedia.com/
https://staging.nobrainermedia.com/services/google-ads-api-integration/
https://staging.nobrainermedia.com/blog/server-side-conversion-attribution/
configPath: ./lighthouserc.json
The lighthouserc.json sets per-page thresholds (LCP < 1500ms, INP < 200ms, CLS < 0.1). For field data, a weekly cron pulls the Chrome UX Report's 28-day rolling p75 for the top URLs:
// scripts/crux-snapshot.ts
const res = await fetch('https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=YOUR_API_KEY', {
method: 'POST',
body: JSON.stringify({
url: 'https://nobrainermedia.com/services/google-ads-api-integration/',
formFactor: 'PHONE',
}),
})
const data = await res.json()
// data.record.metrics.largest_contentful_paint.percentiles.p75
When field data diverges from lab data, real-world network and device conditions are the cause — and tuning to the lab number while the field number stays bad is the failure mode to avoid.
The five failure modes
- Lazy-loading the LCP image.
loading="lazy"on an above-fold hero adds 500ms+. Mark above-fold imagesloading="eager" fetchpriority="high". - Shipping the wrong hydration bundle. A blog page that bundles React because of one contact-page component regresses INP on every blog visit. Audit per-page bundles.
- Deferring critical CSS. If the above-fold layout CSS loads async, LCP suffers. Verify in DevTools that it is inlined in the head.
- A CDN with a poor cache-hit rate. Edge cache hit rate should sit above 95%; below that usually means stale cache-control headers or bypass rules.
- Fonts without
font-display: swap. The default produces a multi-second invisible-text window that hurts both LCP and perceived speed.
Closing
Sub-1.5s LCP, sub-200ms INP, sub-0.1 CLS is the static-first edge-delivery property living up to its claim. The per-page-type tuning above is how you operationalize it across eleven page types — mostly by not regressing what the architecture gives you for free. The boring stack that prints hits these targets at the architectural level; per-page audits at deploy time plus a 28-day CrUX window keep both lab and field honest.
Ready to ship a site that holds sub-1.5s LCP at field scale? Book a 30-minute call →
Get the kit, not just the theory.
We'll send the build checklist behind this post — and the next pillar when it ships. One email, no drip sequence. Unsubscribe in one click.