/ CSS Checker Fixes / Unused CSS

How to Fix Unused CSS

Most production sites ship 60-80% unused CSS — rules for components that don't appear on the current page, classes from libraries that were never used, old code nobody removed. Unused CSS slows page load, blocks rendering, wastes bandwidth on mobile. PurgeCSS and modern build tools can cut bundles by 80-95% in minutes. This guide walks through the measurement, the tooling, and the safelist patterns that prevent breakage.

1. Measure how bad it is

Step 1
Chrome DevTools Coverage
Open DevTools → Cmd/Ctrl+Shift+P → "Show Coverage" → Reload. Coverage tab shows every CSS and JS file with the percentage unused on the current page.

Look for CSS files in the 60-90% unused range — that's where the gains are.
Step 2
Test multiple pages
Coverage shows usage for ONE page. Visit several representative pages (homepage, product, category, blog post). Combine the used classes — that's your real "used" set across the site.

2. Install PurgeCSS

PurgeCSS scans your HTML and JS for class names, then removes CSS rules whose selectors don't appear.

Standalone (PostCSS plugin)

npm install --save-dev @fullhuman/postcss-purgecss

// postcss.config.js
module.exports = {
  plugins: [
    require('@fullhuman/postcss-purgecss')({
      content: ['./src/**/*.html', './src/**/*.js', './src/**/*.jsx'],
      defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
    })
  ]
}

WordPress

Use the Asset CleanUp plugin to unload CSS files page-by-page, or build a custom solution that scans wp_print_styles.

Tailwind

Tailwind v3+ does this automatically via the content config:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js,jsx,ts,tsx}',
    './public/**/*.html',
  ],
  // ...
}

Tailwind only generates classes it sees in the content paths. If your Tailwind build is huge, content paths aren't covering all your templates.

3. Safelist dynamic classes

⚠️ PurgeCSS doesn't see classes added by JavaScript at runtime. Classes like .is-open, .has-error, .dark-mode get removed unless safelisted, breaking your site at runtime.
// postcss.config.js with safelist
require('@fullhuman/postcss-purgecss')({
  content: [...],
  safelist: [
    'is-open',
    'has-error',
    'dark-mode',
    /^bg-(red|green|blue)-(100|200|300|400|500)$/,  // colour utilities used dynamically
  ]
})

Common runtime classes to safelist

4. Split CSS per route

Even after purging, sites still ship "all components combined" CSS. Better: each route loads only the CSS for the components it uses.

Next.js (automatic)

Next.js automatically code-splits CSS per route when you use CSS Modules or styled-jsx. Components imported by a route generate that route's CSS chunk.

Vite / webpack

Use dynamic imports for route-level code splitting:

// React Router lazy routes
const Product = lazy(() => import('./pages/Product'));
const About = lazy(() => import('./pages/About'));

Each lazy-loaded page gets its own JS chunk AND CSS chunk.

5. Inline critical CSS

For fastest First Contentful Paint, inline the CSS needed for above-the-fold content directly in the HTML, defer the rest.

<head>
  <style>
    /* Critical CSS for above-the-fold: 10-20kb max */
    body { font: 16px/1.5 sans-serif; margin: 0; }
    .header { ... }
    .hero { ... }
  </style>
  <link rel="preload" href="/main.css" as="style" 
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/main.css"></noscript>
</head>

Tools: Critical (npm package), Penthouse, or Next.js's built-in critical CSS extraction.

6. Remove dead CSS from source

PurgeCSS is a build-time fix. Source-level cleanup matters too — old unused files take up developer mental space and grow over time.

Step 1
Find unreferenced CSS files
# Find CSS files not imported anywhere
for f in $(find src -name "*.css"); do
  basename=$(basename "$f" .css)
  if ! grep -r "$basename" src/ --include="*.js" --include="*.jsx" --include="*.ts" --include="*.tsx" --include="*.html" --include="*.scss" -q; then
    echo "Unreferenced: $f"
  fi
done
Step 2
Use CSS-in-JS dead-code analysis
Static CSS-in-JS frameworks (vanilla-extract, Linaria) integrate with bundler tree-shaking. Unused style exports get removed automatically.

7. Measure the win

After purging and splitting, re-run DevTools Coverage:

💡 Don't aim for 100% used CSS on every page — that requires per-page generation that adds build complexity. 70-80% used is a great balance between bundle size and build sanity.

🎨 Re-run the CSS Checker

Verify unused-CSS findings have dropped.

Run CSS Checker →
Related Guides: CSS Checker Fixes  ·  Fix CSS Bundle Size  ·  Fix Render-Blocking CSS  ·  CSS Checker Guide
💬 Got a problem?