← Back to Blog

Auditing Core Web Vitals — A Client-Side Approach Without the PSI Quota

Auditing Core Web Vitals — A Client-Side Approach Without the PSI Quota

TL;DR. Core Web Vitals — LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1 — are a Google ranking factor. Field data (CrUX, 28-day rolling real-user) beats lab data for this reason.

The Core Web Vitals Audit is the audit you reach for when you already suspect a problem in this dimension and need a fast, copy-paste-able fix list. It reuses the same chrome as every other jwatte.com tool — deep-links from the mega analyzers, AI-prompt export, CSV/PDF/HTML download — but the checks it runs are narrow and specific to the dimension described above.

Page-weight + render-blocking + image-dimension audit that approximates Core Web Vitals signals (LCP, INP, CLS) without a Google API key.

Why this dimension matters

Core Web Vitals (LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1) are a Google ranking factor. They also gate how often a page can appear in AI Overviews — slow pages get deprioritized in the answer-generation step after the retrieval step. Field data (CrUX, 28-day rolling real-user) beats lab data (Lighthouse) for this reason: Google indexes field metrics, not synthetic ones.

Common failure patterns

  • LCP hero image with loading="lazy" — lazy-loading the LCP element defers it beyond the above-the-fold paint. The rule is: LCP image = no lazy, add fetchpriority="high", preload it in <link rel="preload" as="image"> with the right imagesrcset for responsive variants.
  • CLS from late-loading web fontsfont-display: swap without size-adjust / ascent-override fallback produces a layout shift at font load. Use font-display: optional on non-critical fonts, or match metrics precisely with a fallback stack.
  • INP from long tasks on interaction — a single main-thread task over 50ms blocks next-paint. The fix is breaking up handlers with requestIdleCallback, scheduler.yield(), or post-message loops. Third-party analytics scripts are the most common culprit; defer + Partytown / WebWorker offload helps.
  • TTFB regressions from cold serverless — scheduled functions with per-region cold starts add 300–1500ms to every geographically-distant visitor. Edge functions (Cloudflare Workers, Vercel Edge, Netlify Edge Functions) cut this to ~50ms.

How to fix it at the source

Treat CrUX field data as the authoritative measure; Lighthouse lab data is a proxy. Instrument your own RUM (the web-vitals library emits LCP/CLS/INP/TTFB/FCP to any endpoint) for percentile breakdowns beyond CrUX's 28-day window. For images: preload the LCP, lazy-load everything below the fold, always provide intrinsic width/height. For scripts: defer or async or Web Worker — never sync at the top of <head>.

Thresholds that matter

Signal Target
LCP (Largest Contentful Paint) Good ≤ 2500ms · Needs Improvement 2500–4000ms · Poor > 4000ms (p75 at origin)
INP (Interaction to Next Paint) Good ≤ 200ms · Needs Improvement 200–500ms · Poor > 500ms (p75)
CLS (Cumulative Layout Shift) Good ≤ 0.1 · Needs Improvement 0.1–0.25 · Poor > 0.25
TTFB (Time to First Byte) Good ≤ 800ms · Needs Improvement 800–1800ms · Poor > 1800ms
FCP (First Contentful Paint) Good ≤ 1800ms · Needs Improvement 1800–3000ms · Poor > 3000ms

Example fix

LCP hero image — minimal fix:

<!-- Fail: lazy on LCP, no priority hint, no preload -->
<img src="/images/hero.webp" loading="lazy" alt="Hero">

<!-- Pass: eager, prioritized, preloaded, with srcset for responsive variants -->
<link rel="preload" as="image"
      href="/images/hero-1200.webp"
      imagesrcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1600.webp 1600w"
      imagesizes="100vw">

<img src="/images/hero-1200.webp"
     srcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1600.webp 1600w"
     sizes="100vw"
     width="1600" height="900"
     fetchpriority="high"
     decoding="async"
     alt="Hero image of the product in use">

When to run the audit

  • After a major site change — redesign, CMS migration, DNS change, hosting platform swap.
  • Quarterly as part of routine technical hygiene; the checks are cheap to run repeatedly.
  • Before an investor / client review, a PCI scan, a SOC 2 audit, or an accessibility-compliance review.
  • When a downstream metric drops (rankings, conversion, AI citations) and you need to rule out this dimension as the cause.

Reading the output

Every finding is severity-classified. The playbook is the same across tools:

  • Critical / red — same-week fixes. These block the primary signal and cascade into downstream dimensions.
  • Warning / amber — same-month fixes. Drag the score, usually don't block.
  • Info / blue — context only. Often what a PR reviewer would flag but that doesn't block merge.
  • Pass / green — confirmation. Keep the control in place.

Every audit also emits an "AI fix prompt" — paste into ChatGPT / Claude / Gemini for exact copy-paste code patches tied to your specific stack.

Related tools in this family

Fact-check notes and sources

This post is informational and not a substitute for professional consulting. Mentions of third-party platforms in the tool itself are nominative fair use. No affiliation is implied.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026