# 08 · Core Web Vitals & Performance

> **Target:** [Unstop.com](https://unstop.com)
> **Focus:** LCP, INP, CLS, render-blocking resources, mobile UX

---

## Prompt used

> *Based on the page details I provide, identify likely performance issues affecting load speed, responsiveness, and visual stability. Prioritize fixes that would improve user experience and SEO.*

---

## Core Web Vitals targets (for reference)

| Metric | Good | Needs improvement | Poor |
|---|---|---|---|
| **LCP** Largest Contentful Paint | ≤ 2.5s | 2.5–4.0s | > 4.0s |
| **INP** Interaction to Next Paint | ≤ 200ms | 200–500ms | > 500ms |
| **CLS** Cumulative Layout Shift | ≤ 0.1 | 0.1–0.25 | > 0.25 |

(Source: [web.dev Core Web Vitals](https://web.dev/articles/vitals))

---

## Likely issues (based on public render + DOM inspection patterns)

### 1. LCP bottlenecks on listing / detail pages
- Hero image for opportunity detail isn't `fetchpriority="high"` or preloaded, so it blocks LCP.
- Above-the-fold banner images lack `width`/`height`, triggering late layout.
- Font files loading from 3rd-party CDN without `preconnect`.

**Fix pattern:**
```html
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="image" href="/media/hero.webp" fetchpriority="high">
<img src="/media/hero.webp" width="1200" height="630" alt="..." fetchpriority="high" decoding="async">
```

### 2. INP suffers from heavy hydration / long JS tasks
- Large initial JS bundle from SPA-style framework.
- Filters on listing pages appear to re-render entire list on every click.
- Third-party scripts (analytics, chat, social) loaded synchronously.

**Fix pattern:**
- Code-split per route; defer non-critical bundles.
- Move analytics + chat widget to `async defer` after `load` or behind user interaction.
- Virtualize long listing grids; avoid re-rendering unchanged rows.

### 3. CLS from late-loading elements
- Dynamic banners / promo strips injected client-side push content down.
- Listing cards without fixed aspect-ratio containers for images.
- Font-swap on display-swap webfonts causing text reflow.

**Fix pattern:**
```css
.card-image { aspect-ratio: 16 / 9; background:#eee; }
```
- Reserve space for ads / promo banners with `min-height`.
- Use `font-display: optional` or `swap` with a carefully matched fallback stack.

### 4. Render-blocking resources
- Multiple CSS + JS files in `<head>` without `async`/`defer`.
- Inline critical CSS is not generated per template.

**Fix pattern:**
- Inline ~14KB of critical CSS above the fold; defer the rest with `<link rel="preload" as="style" onload="this.rel='stylesheet'">`.
- Add `defer` to non-critical scripts.

### 5. Mobile-specific issues
- Tap targets near the fold (filter chips, apply button) sometimes <48px tall.
- Sticky elements on short viewports overlap CTA.
- Horizontal scroll on narrow devices from promo strip using `min-width`.

---

## Prioritized fixes (impact × effort)

| # | Fix | Metric improved | Effort |
|---|---|---|---|
| 1 | Add `fetchpriority="high"` + explicit `width`/`height` to hero image on listing + detail templates | LCP | Low |
| 2 | Preconnect + preload webfonts; adopt `font-display: swap` with matched fallback | LCP, CLS | Low |
| 3 | Defer analytics + chat widget scripts (`async defer`, delay to user intent) | INP, LCP | Low |
| 4 | Lock aspect-ratio on card images + reserve space for banners | CLS | Low |
| 5 | Generate + inline critical CSS per template | LCP | Medium |
| 6 | Code-split SPA bundle by route + lazy-load filter components | INP | Medium |
| 7 | Virtualize long listing grids | INP | Medium |
| 8 | Migrate images to WebP/AVIF with responsive `srcset`+`sizes` | LCP | Medium |
| 9 | Audit third-party scripts; remove or gate unused ones | All | High |
| 10 | Upgrade to HTTP/2 or HTTP/3 on static assets + enable Brotli | LCP | Low (if infra supports) |

---

## Measurement plan

1. **Baseline:** run [PageSpeed Insights](https://pagespeed.web.dev) on 5 representative URLs (home, two hubs, two detail pages) — capture both mobile + desktop field data where available.
2. **Lab + field:** combine Lighthouse lab data with CrUX field data from Search Console → Core Web Vitals report.
3. **Per-change monitoring:** run Lighthouse CI in your build pipeline to prevent regressions.
4. **RUM (Real User Monitoring):** consider Vercel Speed Insights, Cloudflare Web Analytics, or a custom `web-vitals` JS integration for live user data.

---

### Further reading

- [web.dev — Core Web Vitals](https://web.dev/articles/vitals)
- [web.dev — Optimize LCP](https://web.dev/articles/optimize-lcp) · [INP](https://web.dev/articles/optimize-inp) · [CLS](https://web.dev/articles/optimize-cls)
- [Google — CrUX Dashboard](https://developer.chrome.com/docs/crux)

Back to: [Schema & Rich Results](./07-schema-rich-results.md) · Next: [Competitor Comparison →](./09-competitor-comparison.md)
