web-quality
Audit web application quality — Core Web Vitals (LCP, FID, CLS), Lighthouse scores, performance optimization, accessibility compliance, SEO, and best practices. Use when the user mentions "performance audit", "Lighthouse", "Core Web Vitals", "LCP", "page speed", "web quality", or needs to improve web app metrics.
From project-orchestratornpx claudepluginhub vivekmano27/agent-orchestrator --plugin project-orchestratorThis skill is limited to using the following tools:
Web Quality Skill
Step 1 — Identify Performance-Critical Pages
Scan for routes and prioritize by traffic impact:
# Find all page/route entry points
find . -path "*/app/**/page.tsx" -o -path "*/pages/**/*.tsx" | grep -v node_modules | head -30
# Find dynamic routes (often slower due to data fetching)
grep -rn "getServerSideProps\|getStaticProps\|generateStaticParams\|loader\|useLoaderData" --include="*.ts" --include="*.tsx" | grep -v node_modules
# Find client-side heavy pages
grep -rn "'use client'\|\"use client\"" --include="*.tsx" | grep -v node_modules
Pages with getServerSideProps (not cached) and 'use client' at the page level are highest priority for optimization.
Step 2 — Configure Lighthouse CI
Create lighthouserc.js in the project root:
module.exports = {
ci: {
collect: {
url: [
'http://localhost:3000/',
'http://localhost:3000/dashboard',
'http://localhost:3000/login',
// Add all critical routes
],
startServerCommand: 'npm run start', // use production build, NOT dev
startServerReadyPattern: 'ready on',
numberOfRuns: 3, // median of 3 runs reduces variance
settings: {
preset: 'desktop', // also run with preset: 'mobile' separately
throttling: {
// Simulate real-world conditions — do NOT test unthrottled
cpuSlowdownMultiplier: 4,
rttMs: 40,
throughputKbps: 10240,
},
},
},
assert: {
assertions: {
// Core Web Vitals — hard gates
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'interactive': ['error', { maxNumericValue: 3800 }],
'total-blocking-time': ['error', { maxNumericValue: 200 }],
// Category scores — hard gates
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'categories:best-practices': ['error', { minScore: 0.9 }],
'categories:seo': ['error', { minScore: 0.9 }],
// Individual audits — warn on regression
'uses-responsive-images': 'warn',
'offscreen-images': 'warn',
'render-blocking-resources': 'warn',
'unused-css-rules': 'warn',
'unused-javascript': 'warn',
},
},
upload: {
target: 'temporary-public-storage', // free Lighthouse CI storage
// Or use: target: 'lhci', serverBaseUrl: 'https://your-lhci-server.com'
},
},
};
Install and run:
npm install -D @lhci/cli
npx lhci autorun
Step 3 — Performance Budget Configuration
Create budget.json for build-time enforcement:
[
{
"path": "/*",
"timings": [
{ "metric": "largest-contentful-paint", "budget": 2500 },
{ "metric": "cumulative-layout-shift", "budget": 0.1 },
{ "metric": "total-blocking-time", "budget": 200 }
],
"resourceSizes": [
{ "resourceType": "script", "budget": 300 },
{ "resourceType": "stylesheet", "budget": 50 },
{ "resourceType": "image", "budget": 500 },
{ "resourceType": "total", "budget": 1000 }
],
"resourceCounts": [
{ "resourceType": "script", "budget": 15 },
{ "resourceType": "third-party", "budget": 5 }
]
}
]
For webpack/Next.js, also configure bundle size alerts:
// next.config.js
module.exports = {
experimental: {
// Fail build if page JS exceeds budget
largePageDataWarnings: true,
},
webpack: (config, { isServer }) => {
if (!isServer) {
config.performance = {
hints: 'error', // fail the build, don't just warn
maxAssetSize: 250_000, // 250 KB per asset
maxEntrypointSize: 300_000, // 300 KB per entry point
};
}
return config;
},
};
Constraint: Performance budgets must use 'error' not 'warning'. Warnings are ignored. A budget that doesn't break the build is not a budget.
Step 4 — Find Optimization Opportunities
Run these searches to find common performance problems:
# Unoptimized images (not using next/image or responsive formats)
grep -rn '<img ' --include="*.tsx" --include="*.jsx" | grep -v 'next/image\|Image ' | grep -v node_modules
# Large client-side bundles (barrel imports)
grep -rn "from '[^']*'" --include="*.ts" --include="*.tsx" | grep "import {" | grep -v node_modules | head -30
# Check for barrel file re-exports that pull entire packages
grep -rn "export \*" --include="index.ts" --include="index.js" | grep -v node_modules
# Synchronous data fetching blocking render
grep -rn "await.*fetch\|await.*axios\|await.*prisma\|await.*db\." --include="*.tsx" | grep -v node_modules
# Multiple sequential awaits in a page component = waterfall
# Missing lazy loading
grep -rn "import.*from" --include="*.tsx" --include="*.jsx" | grep -v "lazy\|dynamic\|Suspense\|node_modules" | head -30
# Web font loading without display swap
grep -rn "@font-face\|font-display" --include="*.css" --include="*.scss" | grep -v node_modules
grep -rn "fonts.googleapis.com" --include="*.html" --include="*.tsx" | grep -v "display=swap"
# Missing rel=preload/preconnect for critical resources
grep -rn "rel=\"preload\"\|rel=\"preconnect\"" --include="*.html" --include="*.tsx" | grep -v node_modules
Step 5 — CI Integration
Add to .github/workflows/lighthouse.yml:
name: Lighthouse CI
on:
pull_request:
paths:
- 'src/**'
- 'app/**'
- 'public/**'
- 'package.json'
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- run: npx @lhci/cli autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: lighthouse-results
path: .lighthouseci/
retention-days: 14
Output Format
Write to .claude/specs/{feature}/web-quality-report.md:
# Web Quality Report — {Feature/Project Name}
**Date:** {YYYY-MM-DD}
**Pages audited:** {count}
**Lighthouse version:** {version}
**Throttling:** {4x CPU slowdown, 40ms RTT, 10Mbps}
## Scores Summary
| Page | Performance | Accessibility | Best Practices | SEO | LCP (ms) | CLS | TBT (ms) |
|------|-------------|---------------|----------------|-----|----------|-----|-----------|
| / | 94 | 98 | 100 | 100 | 1850 | 0.02 | 120 |
| /dashboard | 72 | 91 | 92 | 95 | 3200 | 0.15 | 450 |
## Core Web Vitals
| Metric | Target | Best Page | Worst Page | Status |
|--------|--------|-----------|------------|--------|
| LCP | < 2500ms | / (1850ms) | /dashboard (3200ms) | FAIL |
| CLS | < 0.1 | / (0.02) | /dashboard (0.15) | FAIL |
| TBT | < 200ms | / (120ms) | /dashboard (450ms) | FAIL |
## Bundle Analysis
| Entry Point | Size (gzip) | Budget | Status |
|-------------|-------------|--------|--------|
| pages/index | 142 KB | 300 KB | PASS |
| pages/dashboard | 380 KB | 300 KB | FAIL |
## Optimization Opportunities
### Critical (Blocking CWV Targets)
1. **{Page}: LCP — {cause}**
- **Current:** {value}ms, **Target:** <2500ms
- **Root cause:** {e.g., hero image is 2.4MB uncompressed PNG}
- **Fix:** {exact change — e.g., convert to WebP, add `next/image` with priority, add `<link rel="preload">`}
- **File:** `{path}:{line}`
- **Estimated impact:** -{X}ms
### Recommended
1. **{Optimization}** — {estimated impact}
- **File:** `{path}:{line}`
- **Change:** {specific code/config change}
## Third-Party Script Impact
| Script | Size | Blocking? | Load Time Impact | Recommendation |
|--------|------|-----------|------------------|----------------|
| Google Analytics | 45 KB | No (async) | +120ms | Keep |
| Intercom widget | 210 KB | Yes | +800ms | Lazy-load after interaction |
Constraints
- Always run Lighthouse on a production build (
npm run build && npm start). Dev mode includes hot reload overhead, source maps, and unminified code. Dev-mode Lighthouse scores are meaningless. - Never report Lighthouse scores without specifying throttling settings. Unthrottled scores are 20-40 points higher than real user experience.
- When a CWV metric fails, trace it to a specific resource or code path. "LCP is 3.2s" is a symptom. "LCP is 3.2s because the hero image at
public/hero.pngis 2.4MB and loaded withoutpriority" is actionable. - Do not recommend
loading="lazy"on above-the-fold images. This worsens LCP. Only hero/LCP images should be eagerly loaded. resourceSizesbudgets are in KB (kilobytes), not bytes. Getting this wrong creates budgets that are 1000x too permissive.