Skip to content
This repository has been archived by the owner on Feb 3, 2025. It is now read-only.

Commit

Permalink
Add FCP and TTFB metrics (#131)
Browse files Browse the repository at this point in the history
* Add FCP and TTFB metrics

* Fix crux

* TTFB is a TLA
  • Loading branch information
tunetheweb authored Jul 11, 2023
1 parent d859886 commit 2e105cf
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 11 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ It supports all of the [Core Web Vitals](https://web.dev/vitals/#core-web-vitals
It also supports the newer INP metric:
* [Interaction to Next Paint](https://web.dev/inp/)

Finally, it also supports the diagnostic metrics:
* [Time to First Byte](https://web.dev/ttfb/)
* [First Contentful Paint](https://web.dev/fcp/)

<h3 id="install">Installation Instructions</h3>

The Web Vitals Chrome Extenstion can be installed from the [Chrome Web Store](https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma).
Expand Down Expand Up @@ -123,6 +127,7 @@ We are making available a set of guides for optimizing each of the Core Web Vita
* [Optimize LCP](https://web.dev/optimize-lcp/)
* [Optimize FID](https://web.dev/optimize-fid/)
* [Optimize INP](https://web.dev/optimize-inp/)
* [Optimize TTFB](https://web.dev/optimize-ttfb/)

Lighthouse also includes additional actionability audits for these metrics. They will answer questions like:

Expand Down
Binary file modified media/cwv-extension-drilldown-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified media/cwv-extension-drilldown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified media/cwv-extension-overlay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified media/cwv-extension-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions service_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const LCP_THRESHOLD = 2500;
const FID_THRESHOLD = 100;
const INP_THRESHOLD = 200;
const CLS_THRESHOLD = 0.1;
const FCP_THRESHOLD = 1800;
const TTFB_THRESHOLD = 800;

/**
* Hash the URL and return a numeric hash as a String to be used as the key
Expand Down
40 changes: 36 additions & 4 deletions src/browser_action/core.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.web-vitals-chrome-extension-popup {
width: 560px;
height: 350px;
height: 600px;

font-size: 12px;

Expand Down Expand Up @@ -112,6 +112,7 @@
.web-vitals-chrome-extension-popup .metric-wrapper {
padding: 0rem 1rem;
}

.web-vitals-chrome-extension-popup .metric-wrapper:hover {
background-color: var(--color-light-grey);
}
Expand All @@ -123,7 +124,7 @@
column-gap: 4.17rem;
align-items: center;
border-bottom: 1px solid var(--color-dark-grey);
height: 6.67rem;
height: 6.2rem;
}
.web-vitals-chrome-extension-popup .metric-wrapper:last-child .metric {
border-bottom: none;
Expand All @@ -142,16 +143,47 @@
white-space: nowrap;
}

.web-vitals-chrome-extension-popup .core-web-vital-icon,
.web-vitals-chrome-extension-popup .pending-core-web-vital-icon,
.web-vitals-chrome-extension-popup .experimental-icon {
display: none;
fill: #06f;
vertical-align: middle;
margin-inline-start: 0.2rem;
margin-inline-start: 0.5rem;
fill: #06f;
}

.web-vitals-chrome-extension-popup .pending-core-web-vital-icon {
fill: gray;
}

.web-vitals-chrome-extension-popup #legend .core-web-vital-icon,
.web-vitals-chrome-extension-popup #legend .pending-core-web-vital-icon,
.web-vitals-chrome-extension-popup #legend .experimental-icon {
display: inline-block;
margin-inline-start: 0;
margin-inline-end: 0.5rem;
}

.web-vitals-chrome-extension-popup .core-web-vital-metric .core-web-vital-icon {
display: inline-block;
}

.web-vitals-chrome-extension-popup .pending-core-web-vital-metric .pending-core-web-vital-icon {
display: inline-block;
}

.web-vitals-chrome-extension-popup .experimental-metric .experimental-icon {
display: inline-block;
}


.web-vitals-chrome-extension-popup #legend {
display: flex;
justify-content: space-between;
border: 1px solid var(--color-dark-grey);
padding: 0.5rem;
}

.web-vitals-chrome-extension-popup .metric-performance {
position: relative;
}
Expand Down
1 change: 0 additions & 1 deletion src/browser_action/crux.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Metric } from './metric.js';
// This key only works from the Web Vitals extension.
const CRUX_API_KEY = 'AIzaSyCZKhcAeiqGCp34891LPqVteT5kUMMq1og';


export class CrUX {

static load(pageUrl, formFactor) {
Expand Down
94 changes: 89 additions & 5 deletions src/browser_action/metric.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export class Metric {

constructor({id, name, local, background, thresholds, experimental}) {
constructor({id, name, local, background, thresholds, experimental, coreWebVital, pendingCoreWebVital}) {
this.id = id;
this.abbr = id.toUpperCase();
this.name = name;
Expand All @@ -11,6 +11,8 @@ export class Metric {
// This will be replaced with field data, if available.
this.distribution = [1/3, 1/3, 1/3];
this.experimental = experimental || false;
this.coreWebVital = coreWebVital || false;
this.pendingCoreWebVital = pendingCoreWebVital || false;
}

formatValue(value) {
Expand Down Expand Up @@ -142,7 +144,8 @@ export class Metric {
'first_input_delay': 'fid',
'interaction_to_next_paint': 'inp',
'cumulative_layout_shift': 'cls',
'first_contentful_paint': 'fcp'
'first_contentful_paint': 'fcp',
'experimental_time_to_first_byte': 'ttfb'
};

return nameMap[cruxName];
Expand All @@ -166,7 +169,8 @@ export class LCP extends Metric {
name: 'Largest Contentful Paint',
local,
background,
thresholds
thresholds,
coreWebVital: true
});
}

Expand Down Expand Up @@ -205,7 +209,8 @@ export class FID extends Metric {
name: 'First Input Delay',
local,
background,
thresholds
thresholds,
coreWebVital: true
});
}

Expand Down Expand Up @@ -244,6 +249,7 @@ export class INP extends Metric {
local,
background,
thresholds,
pendingCoreWebVital: true
});
}

Expand Down Expand Up @@ -284,7 +290,8 @@ export class CLS extends Metric {
name: 'Cumulative Layout Shift',
local,
background,
thresholds
thresholds,
coreWebVital: true
});
}

Expand All @@ -293,3 +300,80 @@ export class CLS extends Metric {
}

}

export class FCP extends Metric {

constructor({local, background}) {
const thresholds = {
good: 1800,
poor: 3000
};

// TODO(rviscomi): Consider better defaults.
local = local || 0;

super({
id: 'fcp',
name: 'First Contentful Paint',
local,
background,
thresholds
});
}

formatValue(value) {
value /= 1000;
return this.toLocaleFixed({
value,
unit: 'second'
});
}

getAssessmentIndex() {
return super.getAssessmentIndex();
}

getInfo() {
if (this.background) {
return 'FCP inflated by tab loading in the background';
}

return super.getInfo();
}

}

export class TTFB extends Metric {

constructor({local, background}) {
const thresholds = {
good: 800,
poor: 1800
};

// TODO(rviscomi): Consider better defaults.
local = local || 0;

super({
id: 'ttfb',
name: 'Time to First Byte',
local,
background,
thresholds,
experimental: true
});
}

formatValue(value) {
value /= 1000;
return this.toLocaleFixed({
value,
unit: 'second'
});
}

getAssessmentIndex() {
return super.getAssessmentIndex();
}

}
41 changes: 41 additions & 0 deletions src/browser_action/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ <h1>Metrics</h1>
<title>Experimental metric</title>
<path d="M2.333 12c-.566 0-.969-.253-1.208-.758-.239-.506-.18-.975.175-1.409l3.7-4.5v-4h-.667a.646.646 0 0 1-.475-.192.645.645 0 0 1-.191-.474c0-.19.063-.348.191-.476A.647.647 0 0 1 4.333 0h5.334c.189 0 .347.064.474.191a.646.646 0 0 1 .192.476.643.643 0 0 1-.192.474.643.643 0 0 1-.474.192H9v4l3.7 4.5c.356.434.414.903.175 1.409-.239.505-.642.758-1.208.758H2.333Zm0-1.333h9.334l-4-4.867V1.333H6.333V5.8l-4 4.867Z"/>
</svg>

<svg class="core-web-vital-icon" width="14" height="14" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Core Web Vital metric</title>
<circle cx="48" cy="48" r="48"/>
</svg>

<svg class="pending-core-web-vital-icon" width="14" height="14" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Pending Core Web Vital metric</title>
<circle cx="48" cy="48" r="48"/>
</svg>

</div>

<div class="metric-performance metadata">
Expand Down Expand Up @@ -51,6 +62,35 @@ <h1>Metrics</h1>
</main>

<footer class="metadata">

<div id="legend">

<span>
<svg class="core-web-vital-icon" width="14" height="14" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Core Web Vital metric</title>
<circle cx="48" cy="48" r="48"/>
</svg>
Core Web Vital metric
</span>

<span>
<svg class="pending-core-web-vital-icon" width="14" height="14" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Pending Core Web Vital metric</title>
<circle cx="48" cy="48" r="48"/>
</svg>
Pending Core Web Vital metric
</span>

<span>
<svg class="experimental-icon" width="14" height="12" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>Experimental metric</title>
<path d="M2.333 12c-.566 0-.969-.253-1.208-.758-.239-.506-.18-.975.175-1.409l3.7-4.5v-4h-.667a.646.646 0 0 1-.475-.192.645.645 0 0 1-.191-.474c0-.19.063-.348.191-.476A.647.647 0 0 1 4.333 0h5.334c.189 0 .347.064.474.191a.646.646 0 0 1 .192.476.643.643 0 0 1-.192.474.643.643 0 0 1-.474.192H9v4l3.7 4.5c.356.434.414.903.175 1.409-.239.505-.642.758-1.208.758H2.333Zm0-1.333h9.334l-4-4.867V1.333H6.333V5.8l-4 4.867Z"/>
</svg>
Experimental metric
</span>

</div>

<div id="device-page-wrapper">
<span class="device-icon">
<svg id="device-icon-desktop" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Desktop field data">
Expand All @@ -72,6 +112,7 @@ <h1>Metrics</h1>
<a href="https://web.dev/load-fast-enough-for-pwa/" target="_blank" rel="noopener">Learn more</a>
</span>
</div>

</footer>

<script type="module" src="popup.js"></script>
Expand Down
12 changes: 11 additions & 1 deletion src/browser_action/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import { loadLocalMetrics, getOptions } from './chrome.js';
import { CrUX } from './crux.js';
import { LCP, FID, INP, CLS } from './metric.js';
import { LCP, FID, INP, CLS, FCP, TTFB } from './metric.js';


class Popup {
Expand Down Expand Up @@ -77,6 +77,14 @@ class Popup {
local: this._metrics.inp.value,
background: this.background
});
this.metrics.fcp = new FCP({
local: this._metrics.fcp.value,
background: this.background
});
this.metrics.ttfb = new TTFB({
local: this._metrics.ttfb.value,
background: this.background
});

this.renderMetrics();
}
Expand Down Expand Up @@ -151,6 +159,8 @@ class Popup {
localValue.innerText = metric.formatValue(metric.local);
metricElement.classList.toggle(assessment, !!assessment);
metricElement.classList.toggle('experimental-metric', metric.experimental);
metricElement.classList.toggle('core-web-vital-metric', metric.coreWebVital);
metricElement.classList.toggle('pending-core-web-vital-metric', metric.pendingCoreWebVital);
infoElement.title = info;
infoElement.classList.toggle('hidden', info == '');

Expand Down
Loading

0 comments on commit 2e105cf

Please sign in to comment.