Core Web Vitals are field measurements, not a score
The original LCP, FID, and CLS set made user-centered performance easier to discuss in 2020, provided I kept field distributions, metric limits, and actual product behavior visible.
When Core Web Vitals arrived in May 2020, the Web gained a compact vocabulary for three experiences people already understood: the important content took too long to appear, the page did not respond when touched, and the layout moved under an attempted click.
The original set was Largest Contentful Paint, First Input Delay, and Cumulative Layout Shift. LCP addressed loading, FID addressed the delay before the browser could begin handling the first interaction, and CLS addressed unexpected visual instability.
Their simplicity was useful. It also made them easy to turn into one badge, one benchmark target, or one argument that a page was “fast.”
I added the metrics to D4U7 as field evidence, not as a replacement for performance reasoning. A passing threshold could narrow a question. It could not certify the experience.
The names described user moments
Earlier performance work in my projects had relied on transfer size, request count, server response time, first paint, JavaScript execution, and task duration. Each exposed a part of the path. None alone said when the main visible content felt ready or whether it stayed put.
Core Web Vitals selected outcomes closer to experience:
- Largest Contentful Paint (LCP): when the largest eligible visible content element had painted, used as a proxy for the main content becoming visible.
- First Input Delay (FID): the delay between a person's first eligible interaction and the browser beginning to process its handler.
- Cumulative Layout Shift (CLS): the accumulated impact of unexpected layout movement during the page's life under the original 2020 definition.
The initial guidance classified a good experience at the 75th percentile using thresholds of 2.5 seconds for LCP, 100 milliseconds for FID, and 0.1 for CLS.
Those values created a shared starting point. I kept the plain-language moment beside every chart. Otherwise it was too easy to optimize an acronym while forgetting the experience it approximated.
Lighthouse and browser traces could reproduce a page under controlled device and network assumptions. The control was valuable. It made changes comparable and code paths inspectable.
Field data included conditions I had not imagined: a slower phone, a warm cache, a background tab, an extension, an interrupted network, a font already present, or a person who interacted before the page settled. It supplied distributions rather than one run.
The two sources were not rivals.
Lab measurement answered: under this declared environment, what happened and which code caused it? Field measurement answered: across these observed visits, what experiences occurred and where should I investigate?
A poor field LCP distribution could be reproduced with a throttled lab trace and decomposed into server wait, resource discovery, download, and render delay. A passing lab run could not erase a slow field tail. A field metric could reveal the problem and rarely identify the precise source alone.
I placed field and lab evidence beside each other without averaging them into one value.
A percentile was not an individual experience
The 75th percentile prevented a small number of very fast visits from disguising a slower majority. It still represented a point in a distribution.
If D4U7 had enough comparable samples, a good 75th-percentile LCP would mean at least three quarters of those visits met the selected threshold. It would not mean every participant saw the room quickly. The remaining quarter could include the people on the weakest devices and networks—the conditions the offline-oriented product claimed to respect.
My invited study group was small. I did not publish precise aggregate claims whose sample size could not support them. I inspected distributions and individual traces with consent, used synthetic runs for repeatability, and treated the results as design evidence rather than population statistics.
I segmented only where the sample and product question justified it: route type, mobile or desktop class, cold or repeat navigation, and app version. Splitting by many dimensions would create tiny buckets and privacy risk while producing unstable conclusions.
The threshold was a useful boundary. The distribution and affected path remained the work.
LCP could select the wrong protagonist
On D4U7's entry page, the LCP candidate was sometimes the room heading and sometimes a decorative introductory illustration. Optimizing the illustration's load made the metric improve without helping a participant understand the room sooner.
I removed the illustration from the critical entry path. The room brief, decision state, and open blockers were the meaningful content. Server-rendering that structure and keeping its CSS small improved both the metric and the task.
On another route, an evidence image legitimately became the largest content element. Reserving its dimensions prevented layout movement, but loading every evidence image eagerly delayed the initial decision overview. I prioritized the visible relevant asset and deferred below-the-fold media.
LCP told me which eligible element was largest at the time it painted. It did not decide which content should be important. Candidate changes across viewport sizes could also make an aggregate look like one stable thing when different pages were being measured.
I recorded the candidate element and route revision with the metric. A change in LCP became inspectable rather than an unexplained number.
FID measured the first delay, not sustained responsiveness
First Input Delay was field-only in a fundamental sense: it needed a person to provide a first eligible input. A visit with no interaction produced no FID sample. A person who first clicked after the main thread became idle could see an excellent FID even if the page had been unresponsive earlier.
D4U7 could also pass the first interaction and struggle later when opening a large conflict diff or filtering a long revision history. FID did not measure those later delays.
I kept it for what it could reveal. If someone tried to open a room menu while a large hydration or digest task occupied the main thread, FID captured the wait before handling began. The event target and app phase helped reproduce it.
In the lab, I used main-thread traces and Total Blocking Time as supporting signals, then exercised named interactions under CPU slowdown. I measured later actions separately: open digest, move between changed regions, save a long draft, resolve a conflict, and switch rooms.
A good first input was valuable. It was not a lifetime responsiveness guarantee.
CLS made movement a product defect
Cumulative Layout Shift gave useful weight to a frustration that load-time charts often ignored. A page could paint quickly, then move the action a person was about to select.
D4U7 had several sources:
- Evidence images loaded without reserved aspect ratios.
- A web font changed line wrapping in a decision summary.
- The update banner inserted above the room heading.
- A conflict warning appeared after asynchronous validation.
- A restored draft expanded an editor after the rest of the page rendered.
I fixed them according to interface intent, not merely their contribution score. Images received dimensions or aspect-ratio boxes. Font fallbacks were chosen for closer metrics and critical text did not wait invisibly for a font. Status banners used reserved regions or appeared without moving the active control. Locally restored drafts were read before the editor layout committed where practical.
Some layout changes followed explicit interaction and were expected. The metric's rules tried to distinguish those from unexpected shifts. The product still needed judgment: a technically excluded shift could remain confusing, and a counted shift could be harmless in context.
The score located movement. Watching the flow explained whether it violated the interaction.
The metric definitions were versioned inputs
Web performance metrics evolve. Browser implementations improve, eligible elements change, aggregation rules are refined, and a library release can update calculation details.
I stored more than name and value:
metric name
metric value
metric identifier
measurement library version
application release
route class
navigation type
candidate or interaction target, when available
device and connection class at a coarse levelThe collection date and library version made historical comparisons interpretable. A dashboard did not draw one continuous line across a definition change without an annotation or recomputation plan.
This proved prudent in later years as the Web Vitals set and definitions evolved. A 2020 article should describe FID because FID was the responsiveness Core Web Vital then. Replacing it retroactively with a later metric would make the archive cleaner and the history false.
Metrics are derived artifacts. Their recipe belongs in their identity.
Adding a small web-vitals library made browser collection easy. Q2F8 had taught me that easy collection still needed a declared purpose.
For the private D4U7 study, the purpose was to find loading, initial-interaction, and visual-stability defects across the bounded routes being tested. I did not attach account identity, document content, room title, IP-derived location, or fine-grained hardware fingerprints.
Route classes were coarse. Candidate details were reduced to safe element classifications rather than arbitrary text. Samples had a short retention period and were not joined into participant activity profiles. Study participants knew what was measured.
Low sample volume meant raw per-visit records could become identifying even without a name. Access remained narrow, exports excluded unnecessary request metadata, and analysis favored distributions only when enough comparable events existed.
I also sampled diagnostic detail separately from the core value. Every visit did not need a full performance trace.
Performance evidence should not create a privacy architecture larger than the application it is trying to improve.
I briefly built a page that converted the three metrics into a weighted score. It made releases easy to rank and design tradeoffs easy to obscure.
How should 200 milliseconds of LCP improvement compare with a new layout shift? What weight makes a first-input delay on one visit equivalent to stable rendering across every visit? The formula would encode product priorities while presenting them as arithmetic.
I removed the aggregate. The release view showed each distribution, sample coverage, route classes, and notable regressions. It linked to named performance budgets and user flows.
This prevented compensation. An excellent LCP could not cancel a poor CLS. A release needed to meet the selected conditions independently or carry an explicit exception with a reason.
The same applied to Lighthouse's overall performance number. It was useful for orientation and dangerous as the only acceptance test. Diagnostic audits and underlying measurements told me what changed.
One score is easy to celebrate. Separate claims are easier to act on.
Threshold chasing could damage the page
When a metric approached its threshold, I felt pressure to make the number cross the line. Several quick tactics would have done that at a product cost.
I could render a smaller meaningless element early so it became the LCP candidate. I could delay attaching interaction handlers in a way that changed which input was observed. I could reserve excessive blank space to avoid shifts. I could remove useful update status from the initial view.
The metric definitions resisted some gaming, but no metric can fully encode intent.
I required an optimization to name the user-facing mechanism:
- The room brief becomes visible sooner because its data is fetched with the initial server render.
- The first menu responds sooner because a long digest calculation moved off the main thread.
- The decision action stops moving because its containing region reserves the known update state.
If I could describe only the score change, I had not finished the reasoning.
The threshold guided prioritization. It did not authorize metric-shaped interface tricks.
D4U7 had distinct experiences. The public entry page, authenticated room overview, editor, conflict page, and history search loaded different data and JavaScript.
A site-wide LCP could improve because many light entry pages outweighed a regression in the conflict view. A global FID distribution could contain too few samples from the complex routes to expose them.
I grouped by bounded route class and tracked release changes. Rare but consequential flows kept lab scenarios even when field volume was insufficient. A conflict page did not need thousands of visits before earning a responsiveness budget.
The route taxonomy remained stable across URL details and room IDs. That reduced cardinality and made comparisons meaningful. It also avoided sending private identifiers with telemetry.
When a shared component caused shifts across several routes, the fix could still be global. The evidence began with the affected experience rather than a domain-wide average.
Performance belonged to journeys, not an undifferentiated hostname.
The first fixes were architectural and ordinary
The most valuable D4U7 changes were not specialized Web Vitals tricks:
- Server-render the room's meaningful initial structure.
- Ship less JavaScript on the entry path.
- Defer history and conflict code until the route needed it.
- Move large digest and diff preparation off the main thread.
- Reserve media and async-status geometry.
- Make fonts and icon assets predictable.
- Bound rendered history and preserve accessible navigation.
- Avoid doing projection work twice during hydration.
The metrics helped rank and validate these changes. They did not invent them.
One regression came from an eager client-side search index. It increased JavaScript parsing and occupied the main thread near first interaction. Loading the index only when search opened improved FID samples and reduced ordinary work. The product explanation was stronger than “FID got better”: people who never searched stopped paying for search startup.
Good performance work often removes work rather than executing it more cleverly.
Field dashboards can make missing telemetry look like improvement. A release that breaks the measurement callback may show fewer slow samples because it shows fewer samples.
I tracked expected collection coverage at a coarse level and versioned the collector. A sudden drop created a telemetry warning, not a celebratory empty chart. The page's primary behavior never depended on sending performance data; collection failure remained non-blocking and visible to analysis.
The sendBeacon path and its fallback were tested without copying page content. Delivery used a bounded payload and tolerated page termination. Duplicate metric identifiers were collapsed during ingestion.
No metric appeared when a browser could not support its underlying observation. Unsupported differed from good. FID absence because no eligible interaction occurred differed from collector failure.
The dashboard included sample counts and support coverage beside percentiles. A distribution without its denominator was too easy to overread.
Measurement needed reliability proportional to the decisions made from it.
Accessibility and correctness remained independent budgets
A page could meet every Core Web Vital and trap keyboard focus. It could render stably and announce live updates incoherently. It could respond to the first click quickly and submit a duplicate operation.
D4U7 retained accessibility flows, data-safety scenarios, and correctness tests alongside performance conditions. I did not fold them into the same score or let one compensate for another.
Some fixes interacted. Reserving status space reduced CLS and preserved the relationship between a focused control and its label. Moving work off the main thread improved FID and keyboard response. Server-rendering the room brief improved LCP and made useful content available before client JavaScript.
Other trades needed care. Windowing a long history could reduce DOM cost and harm findability or screen-reader position if implemented poorly. Deferring code could create a new wait at the moment an uncommon action began. The release scenario tested the complete interaction.
User-centered metrics were helpful precisely because they invited product reasoning. They did not replace it.
For each metric, the internal view asked:
- Which user moment does this represent?
- What share of relevant visits produced a sample?
- Which route and app versions contribute to the slow or unstable tail?
- What is the candidate element or interaction target?
- Can the behavior be reproduced under a declared lab condition?
- Which product or architecture change would improve the moment?
- What experience does this metric not measure?
The last question prevented the dashboard from becoming a definition of quality.
LCP did not measure whether the content was correct or useful. FID did not measure later interaction responsiveness. CLS did not judge every expected animation or detect a confusing stable layout. The 75th percentile did not describe every participant. A passing set did not mean the application worked offline or preserved a draft.
The metrics made three important experiences measurable in the field. Their limitations were part of using them well.
A vital sign is evidence, not a verdict
The phrase “Web Vitals” was apt. A vital sign can reveal a problem, track a response, and justify deeper investigation. It does not summarize an entire organism.
In 2020, LCP, FID, and CLS gave me a clearer way to connect browser behavior with loading, first response, and visual stability. They moved performance conversation closer to the person using the page and away from transfer-size trivia alone.
I kept their distributions, versions, candidates, sample coverage, and blind spots visible. I used lab traces to find mechanisms and field observations to find experiences. I refused the convenience of one score.
The best outcome was not a green badge. It was a room whose meaningful state appeared sooner, whose first action responded when attempted, and whose interface stopped moving under attention.