Problem: CSS Is Slowing Your Site and You Don't Know Why
Your Lighthouse score dropped to 60, users complain about janky scrolling, and your CSS bundle is 400KB. Traditional debugging takes hours of trial-and-error.
You'll learn:
- How to use AI to identify performance bottlenecks in CSS
- Automated techniques to fix Cumulative Layout Shift (CLS)
- Reduce CSS bundle size by 40% with AI-assisted purging
Time: 12 min | Level: Intermediate
Why This Happens
Modern CSS frameworks ship with thousands of unused rules. Meanwhile, poorly scoped selectors cause expensive reflows, and missing dimensions trigger layout shifts - but finding the culprits manually is like debugging minified code.
Common symptoms:
- CLS score > 0.1 (Google penalizes in search rankings)
- Main thread blocked for 200ms+ during style recalculation
- CSS files over 100KB after gzip
- Layout jumps when images or fonts load
Solution
Step 1: Generate a Performance Baseline
# Install Chrome DevTools with AI analysis (requires Chrome 122+)
npx lighthouse https://yoursite.com --view \
--only-categories=performance \
--output=json \
--output-path=./baseline.json
Expected: JSON report showing CLS, FCP, and LCP metrics
If it fails:
- Error: "Command not found": Install with
npm i -g lighthouse - Port issues: Add
--chrome-flags="--headless"for CI environments
Step 2: AI-Assisted Layout Shift Detection
Use Claude or similar LLM with vision to analyze your page:
# Capture screenshots during page load
npx playwright test --trace on
# Extract frames from trace
npx playwright show-trace trace.zip
AI Prompt for Claude:
I'm attaching screenshots of my page loading. Identify:
1. Elements causing layout shifts (show pixel measurements)
2. Missing width/height attributes on images
3. Font loading that causes FOUT/FOIT
Provide specific CSS fixes with before/after examples.
Why this works: AI can visually compare frames faster than manual inspection, spotting 1-2px shifts humans miss.
Step 3: Optimize Selectors with AI
// save as analyze-css.ts
import { parse } from 'css-tree';
import fs from 'fs';
const css = fs.readFileSync('./dist/styles.css', 'utf8');
const ast = parse(css);
// Export selector complexity data
const selectors: any[] = [];
ast.children.forEach((rule: any) => {
if (rule.type === 'Rule') {
rule.prelude.children.forEach((selector: any) => {
selectors.push({
text: selector.toString(),
specificity: calculateSpecificity(selector), // Custom function
length: selector.toString().length
});
});
}
});
console.log(JSON.stringify(selectors, null, 2));
Run analysis:
npx tsx analyze-css.ts > selectors.json
AI Prompt:
Analyze this CSS selector data [paste selectors.json].
Show me:
1. Over-specific selectors (specificity > 30)
2. Expensive universal selectors (*)
3. Refactoring suggestions to reduce complexity
Format as: Old selector → New selector (reason)
Expected output from AI:
/* Before: Specificity = 43 */
.header nav ul li.active a.button span { color: blue; }
/* After: Specificity = 11 - AI suggests utility class */
.nav-active-btn { color: blue; }
Step 4: Automated CSS Purging
// tailwind.config.js or postcss.config.js
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx,html}',
// AI scans component usage
],
plugins: [
require('@fullhuman/postcss-purgecss')({
content: ['./src/**/*.{html,js,jsx,tsx}'],
safelist: {
// AI generates this list by analyzing dynamic classes
standard: [/^data-/, /^aria-/, /hljs/],
deep: [/modal$/, /tooltip/],
},
blocklist: [/debug/, /test-/] // AI finds unused test classes
})
]
}
Use AI to find dynamic classes:
Prompt:
Scan my React components [attach src/ folder] and list:
1. Classes generated dynamically (template literals, state-based)
2. Third-party library classes I'm using
3. Classes safe to purge (only in commented code)
Format as PurgeCSS safelist config.
Run build:
npm run build
# Check reduction
ls -lh dist/styles.css
Expected: 400KB → 180KB (55% reduction)
Step 5: Fix Critical Layout Shifts
AI will identify shifts like:
<!-- Before: Causes 0.15 CLS -->
<img src="hero.jpg" alt="Hero">
<!-- After: AI suggests aspect-ratio -->
<img src="hero.jpg" alt="Hero"
width="1200"
height="630"
style="aspect-ratio: 1200/630; max-width: 100%; height: auto;">
For fonts (AI recommendation):
/* Before: FOUT causes shift */
@import url('https://fonts.googleapis.com/css2?family=Inter');
/* After: AI suggests font-display + size-adjust */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* Prevents invisible text */
size-adjust: 107%; /* Matches fallback metrics - AI calculates this */
ascent-override: 90%;
descent-override: 22%;
}
body {
font-family: Inter, system-ui, sans-serif;
}
How AI calculates size-adjust:
Prompt:
My custom font is Inter. The fallback is system-ui.
Calculate size-adjust, ascent-override, descent-override
to minimize layout shift during font swap.
Provide CSS @font-face declaration.
AI uses Capsize or similar algorithms to match x-height and baseline.
Step 6: Continuous Monitoring
# .github/workflows/css-perf.yml
name: CSS Performance Check
on: [pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build CSS
run: npm run build
- name: Check bundle size
run: |
SIZE=$(stat -f%z dist/styles.css)
if [ $SIZE -gt 200000 ]; then
echo "CSS too large: ${SIZE} bytes (max 200KB)"
exit 1
fi
- name: AI Analysis
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_KEY }}
run: |
# Send CSS to Claude for regression check
npx ai-css-audit dist/styles.css \
--compare=baseline.json \
--fail-on-regression
The AI audit script (pseudocode):
// Compares new CSS against baseline
// AI flags new expensive selectors or size increases
const anthropic = new Anthropic();
const newCSS = fs.readFileSync('dist/styles.css');
const baseline = JSON.parse(fs.readFileSync('baseline.json'));
const analysis = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
messages: [{
role: "user",
content: `Compare this CSS [${newCSS}] to baseline [${baseline}].
Flag regressions in:
1. Bundle size (>10% increase)
2. Selector complexity (new selectors with specificity >30)
3. New layout shift risks (missing dimensions)
Output: JSON with pass/fail and specific issues`
}]
});
if (analysis.regression) process.exit(1);
Verification
Test layout shifts:
# Run Lighthouse CI
npx lhci autorun --config=lighthouserc.json
You should see:
- CLS score < 0.1 (was 0.25)
- CSS bundle < 200KB (was 400KB)
- Style recalculation < 50ms (was 200ms)
Verify with real users:
// Add to your site
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.hadRecentInput) continue; // Ignore user-caused shifts
console.log('Layout shift:', entry.value, entry.sources);
// Send to analytics
gtag('event', 'cls', { value: entry.value });
}
}).observe({ type: 'layout-shift', buffered: true });
What You Learned
- AI can visually analyze layout shifts faster than manual inspection
- Automated selector analysis finds expensive CSS patterns
- PurgeCSS with AI-generated safelists reduces bundles by 40-60%
- Font metrics tuning eliminates FOUT layout shifts
Limitations:
- AI may miss framework-specific dynamic classes (manually add to safelist)
- Vision models need clear screenshots (use consistent viewport size)
- Font size-adjust calculations assume system-ui fallback
Tested with Chrome 122, Lighthouse 12.x, Claude Sonnet 4, PurgeCSS 6.x