---
name: "performance_budgets"
description: "Set, measure, and enforce performance budgets for web pages an agent ships: size limits, asset counts, and timing targets, with curl-based measurement and concrete reduction tactics. Triggers on: performance budget, page weight, make my page faster, web vitals, Lighthouse, slow page, reduce bundle size."
metadata: { "includeInPrompt": true }
---

## Purpose

Teach an agent that builds and deploys HTML pages to define a **performance budget** (size limits, asset counts, timing targets), measure a page against it with real tools, and apply concrete reductions to stay under it. This is about shipping fast pages — setting the limits, proving compliance, and fixing overshoot — not about search rankings (see `seo_basics`).

## Workflow

1. **Set the budget before you build.** A budget is 3–5 hard numbers, written down *before* any code ships. Example starting points (tune per page):
   - HTML document ≤ 100 KB transferred; total page weight ≤ 1 MB (≤ 500 KB for mobile-critical pages)
   - ≤ 50 requests on first load; webfonts ≤ 2 families / ≤ 150 KB total; third-party scripts ≤ 3
   - Timing targets: verify current Core Web Vitals thresholds at web.dev/articles/vitals before quoting them — verified 2026-09-20: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1 ("good" at the 75th percentile of real users). INP replaced FID in March 2024; any tool or guide still quoting FID is stale.

2. **Measure with real tools.** Prefer field data (real users, e.g. CrUX / PageSpeed Insights) once the page is live; use lab data (Lighthouse CLI if installed: `lighthouse <url> --only-categories=performance`) to debug *causes*. When no tooling is available, this curl weight check catches the common sins (executed and verified 2026-09-20):

```bash
URL="https://example.com/"   # replace with your page
curl -sS --compressed -D /tmp/hdrs -o /tmp/body -w \
  'code=%{http_code} ttfb=%{time_starttransfer}s total=%{time_total}s bytes=%{size_download} type=%{content_type}\n' "$URL"
# inventory subresources, ranked by transferred bytes
BASE="https://example.com"   # same as your page
grep -oiE '"((/[^"]*|https?://[^"]*)\.(js|css|png|jpe?g|webp|avif|svg|woff2?))"' /tmp/body \
  | tr -d '"' | sort -u | while read -r p; do
  case "$p" in /*) u="$BASE$p";; *) u="$p";; esac
  s=$(curl -sS --compressed -o /dev/null -w '%{size_download}' "$u")
  echo "$s $u"
done | sort -rn | head -15
```

   The `--compressed` flag measures what a real browser receives (gzip/brotli on the wire), not the raw file size — comparing uncompressed files to your budget inflates the numbers. The inventory's line count is your request count; the ranked list is your hit list.

3. **Reduce in the order that pays.** Work the ranked inventory top-down — a single 2 MB og-image outranks ten 5 KB scripts:
   - **Images:** serve modern formats (AVIF/WebP with JPEG/PNG fallback via `<picture>`), resize to the display size (never ship a 2400px photo into a 600px slot), `loading="lazy"` on everything below the fold — never on the hero/LCP element.
   - **Fonts:** subset to the character sets you actually use, `font-display: swap`, limit families and weights; system fonts beat webfonts when the look allows it.
   - **JavaScript:** `defer` non-critical scripts (parser continues, script runs after parse), `async` for fully independent scripts, and cut what isn't needed for first paint. Third-party embeds (analytics, chat widgets) are the usual budget-killers — load them after the `load` event or on user interaction instead.
   - **CSS:** inline critical CSS for above-the-fold content, defer the rest; strip unused rules.
   - **Cache:** versioned filenames + long `max-age` on static assets, so repeat visits barely remeasure.

4. **Enforce the budget so it survives.** Measure on every deploy — a cron or CI check that fails the build when the budget is exceeded. A budget you don't measure is decoration. Record the numbers somewhere durable (a line in the deploy log, a `perf-budget.txt` in the repo) so drift is visible instead of silent.

## Output Contract

After applying this skill, an agent should produce:

- A written budget: 3–5 concrete limits (sizes, counts, timing targets) for the page, decided before shipping.
- A measurement run: the command(s) used, the numbers measured, and which budget lines passed or failed.
- A reduction list: the concrete changes made (or proposed), ordered by impact, with before/after numbers.

## Operating Rules

- **Budget first, code second.** Set the limits before building, not after measuring.
- **Measure transferred bytes.** Always use `--compressed` (or DevTools' "transferred" column); raw file size is the wrong number.
- **One page, one budget.** Don't hold a dashboard to the homepage's budget — different pages earn different budgets.
- **Fix biggest first.** Work the ranked inventory top-down; don't optimize the favicon while a 2 MB PNG loads above the fold.
- **Never lazy-load the LCP element.** `loading="lazy"` above the fold is a classic self-inflicted regression; reserve `fetchpriority="high"` for the hero.
- **Field over lab.** When CrUX/field data disagrees with Lighthouse, trust field data — the lab is synthetic. Google's thresholds apply at the 75th percentile of real users, segmented mobile vs desktop.
- **Thresholds change.** If you quote Core Web Vitals numbers, re-verify them at web.dev/articles/vitals first. Never cite FID as current — it was replaced by INP in March 2024.
