Add comprehensive metrics and engines documentation
Complete the documentation suite with: - Deep-dive metrics reference (LCP, FCP, CLS, TBT, TTFB) - Detailed testing engines comparison (Sitespeed vs PSI) - Why TBT is the killer metric for rds.ink - How to fix each metric using Hummingbird - Score differences and when to use each engine Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
326
docs/03-metrics-reference.md
Normal file
326
docs/03-metrics-reference.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# Performance Metrics Reference
|
||||
|
||||
Deep-dive reference for each Core Web Vital metric captured by the seo-intel system.
|
||||
|
||||
## The Five Metrics
|
||||
|
||||
| Metric | Full Name | Unit | Good | Poor | What Matters |
|
||||
|--------|-----------|------|------|------|--------------|
|
||||
| **LCP** | Largest Contentful Paint | milliseconds | ≤2,500ms | ≥4,000ms | When main content appears |
|
||||
| **FCP** | First Contentful Paint | milliseconds | ≤1,800ms | ≥3,000ms | When ANY content appears |
|
||||
| **CLS** | Cumulative Layout Shift | unitless (0-1) | ≤0.1 | ≥0.25 | How much page jumps |
|
||||
| **TBT** | Total Blocking Time | milliseconds | ≤200ms | ≥600ms | JS blocking interactions |
|
||||
| **TTFB** | Time to First Byte | milliseconds | ≤800ms | ≥1,800ms | Server response speed |
|
||||
|
||||
---
|
||||
|
||||
## 1. LCP — Largest Contentful Paint
|
||||
|
||||
### Definition
|
||||
The time when the **largest visible element** (image, heading, paragraph block, video) appears on screen.
|
||||
|
||||
### Why It Matters
|
||||
Users need to see that the page is loading. LCP is the best metric for "when does the user perceive the page is starting to load?"
|
||||
|
||||
### Thresholds
|
||||
- **≤ 2.5 seconds:** Good — user feels the page is responding
|
||||
- **2.5 – 4.0 seconds:** Needs improvement
|
||||
- **≥ 4.0 seconds:** Poor — user thinks the page is slow/broken
|
||||
|
||||
### What Affects LCP
|
||||
1. **Server response time (TTFB)** — If the server is slow, everything downstream is slow
|
||||
2. **Large images/videos above the fold** — Unoptimized media delays LCP
|
||||
3. **Render-blocking JavaScript** — `<script>` in `<head>` delays rendering
|
||||
4. **Render-blocking CSS** — Large CSS files in `<head>` delay rendering
|
||||
5. **Font loading** — Web fonts block text rendering (can use `font-display: swap`)
|
||||
|
||||
### How to Fix
|
||||
1. **Optimise TTFB** (server response)
|
||||
- Cache dynamic pages
|
||||
- Optimise database queries
|
||||
- Use a CDN for static content
|
||||
2. **Lazy-load below-the-fold images**
|
||||
- Use `loading="lazy"` on `<img>` tags
|
||||
- Hummingbird has automatic image lazy-loading
|
||||
3. **Defer non-critical JavaScript**
|
||||
- Add `defer` attribute to scripts that aren't needed for initial render
|
||||
- Move analytics/tracking to the bottom
|
||||
4. **Critical CSS inlining** (advanced)
|
||||
- Inline the CSS needed for above-the-fold content
|
||||
- Defer the rest with `<link rel="preload">`
|
||||
|
||||
---
|
||||
|
||||
## 2. FCP — First Contentful Paint
|
||||
|
||||
### Definition
|
||||
The time when the browser paints the **first piece of non-whitespace content** to the screen. This could be:
|
||||
- Text
|
||||
- An image
|
||||
- An SVG
|
||||
- A coloured background
|
||||
- Anything that's not white
|
||||
|
||||
### Why It Matters
|
||||
FCP is the user's first visual cue that the page is loading. It happens before LCP.
|
||||
|
||||
### Timeline Relationship
|
||||
```
|
||||
0ms: User clicks link
|
||||
|
|
||||
50ms: Server starts responding
|
||||
|
|
||||
144ms (TTFB): Browser receives first bytes
|
||||
|
|
||||
2,116ms (FCP): Browser paints first content (this page)
|
||||
|
|
||||
2,500ms (LCP): Browser paints largest content
|
||||
|
|
||||
4,000ms: Page is fully interactive
|
||||
```
|
||||
|
||||
### Thresholds
|
||||
- **≤ 1.8 seconds:** Good
|
||||
- **1.8 – 3.0 seconds:** Needs improvement (this page is at 2.1s)
|
||||
- **≥ 3.0 seconds:** Poor
|
||||
|
||||
### What Affects FCP
|
||||
1. **TTFB** — Server has to respond first
|
||||
2. **HTML parsing** — Browser must parse HTML to find content
|
||||
3. **Render-blocking resources** — CSS/JS in `<head>` delay rendering
|
||||
4. **Font loading** — If fonts are slow, text doesn't paint until fonts arrive
|
||||
|
||||
### How to Fix
|
||||
Same as LCP:
|
||||
1. Optimise TTFB
|
||||
2. Defer render-blocking resources
|
||||
3. Lazy-load heavy assets
|
||||
|
||||
---
|
||||
|
||||
## 3. CLS — Cumulative Layout Shift
|
||||
|
||||
### Definition
|
||||
**Measure of unwanted layout changes** after the page is visually complete.
|
||||
|
||||
Example: You're reading an article, about to click a button, and an ad loads above the button, pushing the button down. You accidentally click the ad instead. That's layout shift.
|
||||
|
||||
### Why It Matters
|
||||
CLS directly impacts **user experience frustration**. Unexpected layout changes are one of the most annoying things on the web.
|
||||
|
||||
### Measurement
|
||||
```
|
||||
CLS = sum of all individual layout shifts
|
||||
|
||||
Each shift is: (fraction of viewport moved) × (distance moved)
|
||||
|
||||
Example:
|
||||
- Ad loads, pushes button down by 50px
|
||||
- Viewport is 800px tall
|
||||
- Shift score = (0.5 × 800) / 800 = 0.5
|
||||
|
||||
If this happens once, CLS = 0.5
|
||||
If it happens three times (each 0.5), CLS = 1.5
|
||||
```
|
||||
|
||||
Shifts that happen > 500ms after user input are excluded (they don't surprise the user).
|
||||
|
||||
### Thresholds
|
||||
- **≤ 0.1:** Good — page is stable
|
||||
- **0.1 – 0.25:** Needs improvement — some shifts happening
|
||||
- **≥ 0.25:** Poor — page is jumpy
|
||||
|
||||
### rds.ink Status: 0.0 = PERFECT ✓
|
||||
|
||||
This page does NOT shift after load. The images load lazily, product cards maintain their size, nothing pops up. Great job.
|
||||
|
||||
### What Causes CLS
|
||||
1. **Unset image/video dimensions** — Browser doesn't know how much space to reserve
|
||||
2. **Ads/widgets loading after page render** — Third-party content shifts layout
|
||||
3. **Custom fonts** — Text changes size when font finishes loading
|
||||
4. **Embeds/iframes** — External content pushes layout
|
||||
5. **Animations that move elements** — Animation changing `top` / `left` / `margin`
|
||||
|
||||
### How to Fix
|
||||
1. **Set dimensions on images**
|
||||
```html
|
||||
<img src="..." width="800" height="600" />
|
||||
<!-- or in CSS -->
|
||||
img { aspect-ratio: 800 / 600; }
|
||||
```
|
||||
2. **Reserve space for ads/lazy content**
|
||||
```html
|
||||
<div style="width: 300px; height: 250px;">
|
||||
<!-- ad will load here -->
|
||||
</div>
|
||||
```
|
||||
3. **Use `font-display: swap`**
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'Custom';
|
||||
src: url(...);
|
||||
font-display: swap; /* show fallback first, swap when custom loads */
|
||||
}
|
||||
```
|
||||
4. **Animations: use `transform` instead of `top`/`left`**
|
||||
```css
|
||||
/* GOOD: transform doesn't trigger layout recalc */
|
||||
@keyframes slide {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(100px); }
|
||||
}
|
||||
|
||||
/* BAD: left does trigger layout recalc */
|
||||
@keyframes slide {
|
||||
from { left: 0; }
|
||||
to { left: 100px; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. TBT — Total Blocking Time 🔴 **MOST COMMON PROBLEM**
|
||||
|
||||
### Definition
|
||||
**How long JavaScript is executing on the main thread, blocking all user interactions.**
|
||||
|
||||
The browser can only do one thing at a time on the main thread:
|
||||
- Parse HTML
|
||||
- Execute JavaScript
|
||||
- Render CSS
|
||||
- Handle user input
|
||||
|
||||
If JavaScript is running, the browser **cannot** respond to clicks, scrolls, or keypresses.
|
||||
|
||||
### Why It Matters
|
||||
A page with high TBT **feels frozen**. User clicks a button, nothing happens for 1+ seconds. The page is technically loaded, but unusable.
|
||||
|
||||
### Measurement
|
||||
```
|
||||
JavaScript task execution timeline:
|
||||
|
||||
0ms ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 200ms (long task, blocks for 200ms)
|
||||
User clicks button during this
|
||||
but browser doesn't respond
|
||||
|
||||
200ms (JavaScript done) → Click now registers (if still there)
|
||||
```
|
||||
|
||||
TBT = sum of all "blocking" time. A task is "blocking" if it takes > 50ms.
|
||||
|
||||
For example:
|
||||
- 5 JavaScript tasks, each 100ms = TBT of 5 × 50ms = 250ms blocking
|
||||
- 2 JavaScript tasks, each 1,000ms = TBT of 2 × 950ms = 1,900ms blocking
|
||||
|
||||
### Thresholds
|
||||
- **≤ 200ms:** Good — page feels snappy
|
||||
- **200 – 600ms:** Needs improvement — noticeable sluggishness
|
||||
- **≥ 600ms:** Poor — page feels frozen
|
||||
|
||||
### rds.ink Status: 1,807ms = CRITICAL 🔴
|
||||
|
||||
The page has **1,800ms of JavaScript execution blocking interactions**. During page load, the user cannot interact for 1.8 seconds.
|
||||
|
||||
This is why the score is 77 instead of 95.
|
||||
|
||||
### What Causes High TBT
|
||||
1. **Large JavaScript bundles** (1.8 MB on this page)
|
||||
2. **Synchronous JavaScript execution** — No chunking or deferring
|
||||
3. **Too many plugins** — Each plugin adds code to parse/execute
|
||||
4. **Unoptimised heavy libraries** — jQuery, older frameworks
|
||||
5. **No code-splitting** — Entire app loads upfront instead of as-needed
|
||||
|
||||
### How to Fix (for WordPress/Hummingbird)
|
||||
1. **Defer non-critical JavaScript** (add `defer` attribute)
|
||||
- Page renders first
|
||||
- Scripts load in background
|
||||
- TBT moves to after page is interactive
|
||||
2. **Lazy-load heavy plugins** (load only when needed)
|
||||
- Gallery/lightbox: load when user clicks product
|
||||
- Booking widget: load only on booking page
|
||||
3. **Disable unused plugins** (every plugin = more JS)
|
||||
4. **Code-split large bundles** (Webpack/bundler feature)
|
||||
- Don't load everything upfront
|
||||
- Load only what's visible
|
||||
5. **Minify/compress JavaScript** (reduce parse time)
|
||||
|
||||
### Expected Impact of Fixes
|
||||
- Current: 1,807ms blocking
|
||||
- After defer JS: ~400ms
|
||||
- After lazy-load: ~150ms
|
||||
- After disabling unused: ~100ms
|
||||
- **Target: <200ms**
|
||||
|
||||
---
|
||||
|
||||
## 5. TTFB — Time to First Byte
|
||||
|
||||
### Definition
|
||||
The time from when the browser makes a request until the server sends the first byte of the response.
|
||||
|
||||
```
|
||||
User hits link at 0ms
|
||||
↓
|
||||
0-50ms: Network latency
|
||||
↓
|
||||
50-100ms: Server processes request
|
||||
↓
|
||||
100-144ms: Server sends response (for this page)
|
||||
↓ This is TTFB
|
||||
144ms: Browser receives first byte
|
||||
```
|
||||
|
||||
### Why It Matters
|
||||
TTFB is a **server-side metric**. It measures how fast your infrastructure is.
|
||||
|
||||
Everything downstream depends on TTFB. You can't optimise FCP if TTFB is 2 seconds.
|
||||
|
||||
### Thresholds
|
||||
- **≤ 0.8 seconds:** Good
|
||||
- **0.8 – 1.8 seconds:** Needs improvement
|
||||
- **≥ 1.8 seconds:** Poor
|
||||
|
||||
### rds.ink Status: 144ms = EXCELLENT ✓
|
||||
|
||||
The server responds in 144ms. This is good. Not the bottleneck.
|
||||
|
||||
### What Affects TTFB
|
||||
1. **Server processing time** — Database queries, rendering, etc.
|
||||
2. **Network latency** — Distance from client to server
|
||||
3. **Server hardware** — CPU/RAM/I/O speed
|
||||
4. **Caching** — Is the page/response cached?
|
||||
5. **DNS resolution** — Domain lookup time
|
||||
|
||||
### How to Fix
|
||||
1. **Enable page caching** (Hummingbird)
|
||||
- Cached pages: TTFB 50ms
|
||||
- Uncached: TTFB 144ms
|
||||
2. **Optimise database queries** (most common bottleneck)
|
||||
- Use database indexing
|
||||
- Avoid N+1 queries
|
||||
- Query Monitor plugin helps diagnose
|
||||
3. **Use a CDN for static assets**
|
||||
- CSS, JS, images served from fast edge servers
|
||||
4. **Upgrade hosting** (if server is slow)
|
||||
- More CPU cores
|
||||
- Faster SSD storage
|
||||
- Better network connection
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Metric | Causes | Quick Fixes |
|
||||
|--------|--------|-----------|
|
||||
| **LCP** | Slow TTFB, lazy images, render-blocking JS | Defer JS, optimize TTFB |
|
||||
| **FCP** | Slow TTFB, render-blocking resources | Defer JS, lazy load |
|
||||
| **CLS** | Unset dimensions, ads, fonts, animations | Set dimensions, use `transform` |
|
||||
| **TBT** (THE PROBLEM) | Large JS, too many plugins, sync code | Defer JS, lazy-load plugins, disable unused |
|
||||
| **TTFB** | Slow server, no cache, slow DB | Enable cache, optimize queries |
|
||||
|
||||
---
|
||||
|
||||
See also:
|
||||
- [Score Calculation](02-score-calculation.md) — How these metrics become a 0-100 score
|
||||
- [Testing Engines](04-testing-engines.md) — How metrics are captured
|
||||
- [Case Study: rds.ink 77](../case-studies/rds-77-score.md) — Real example with fixes
|
||||
Reference in New Issue
Block a user