What Are Unused CSS Rules? Complete Guide (2026)

Saar Twito8 min read
Saar Twito
Saar TwitoFounder & SEO Engineer

Hi, I'm Saar - a software engineer, SEO specialist, and lecturer who loves building tools and teaching tech.

View author profile →

What Are Unused CSS Rules?

Unused CSS is selectors and rules that the browser downloads, parses, and adds to the CSSOM but that never apply to a single element on the page. Performance audits flag a stylesheet when its unused bytes exceed 20 KB or 50% of the file. Because CSS is render-blocking, every byte of unused CSS directly delays First Contentful Paint and Largest Contentful Paint.

Key Facts (TL;DR)

  • Audit threshold: Flagged when > 20 KB or > 50% of a stylesheet's bytes are unused.
  • Industry baseline: The median page ships 60–70% unused CSS — the largest waste category by percentage of any web resource (HTTP Archive Web Almanac, CSS chapter).
  • Render-blocking: CSS halts rendering until every linked stylesheet is downloaded and parsed. On a 3G mobile connection, ~50 KB of unused CSS adds roughly ~150 ms to FCP.
  • Affected metrics: First Contentful Paint, Largest Contentful Paint, and overall page weight.
  • Top causes: Loading a full CSS framework when using only a few components, site-wide stylesheets imported on every route, and CMS themes that ship every conceivable variant.

Think of unused CSS like installing a 200-volume encyclopedia just to look up one fact. The browser still has to shelve every volume (parse it into the CSSOM) before it's allowed to start reading — even though 70% of the volumes will never get opened.

Why Unused CSS Hurts Performance and Rankings

CSS is render-blocking by design — the browser refuses to paint anything until it has the complete CSSOM, otherwise styles would flash on and off as new rules arrived. That means every byte of CSS, used or not, sits directly on the critical path to first paint.

  • Delays First Contentful Paint.FCP can't fire until all linked stylesheets are downloaded and parsed. ~50 KB of unused CSS over 3G adds ~150 ms before the user sees anything.
  • Delays Largest Contentful Paint.LCP is the largest visible element — typically text or a hero image — and it can't paint until CSS is ready either.
  • Inflates the CSSOM. The browser must build a full DOM-like tree of every selector. More rules = more memory and slightly slower style recalculations on every layout pass.
  • Eats mobile bandwidth. On metered or slow connections, every kilobyte counts. Cutting a 200 KB stylesheet to 60 KB by purging unused rules is a one-time win that pays off on every visit.
  • Drags down rankings. Core Web Vitals (LCP, INP, CLS) are confirmed Google ranking signals. A page that fails LCP at the 75th percentile loses ranking ground in competitive queries.

Where Unused CSS Comes From

Most unused CSS comes from a small number of structural causes, and the right fix depends on which one you have.

  • Whole-framework CSS imports. Importing a full UI framework (Bootstrap, Bulma, Foundation, etc.) when you use 10 components from it. The framework ships rules for hundreds of components you never reference.
  • Site-wide stylesheets on every route. A single main.css linked from every page contains styles for the homepage, the checkout flow, the admin panel, and the blog — but each visit only renders one of those.
  • CMS themes with every variant. Themes ship CSS for every layout, color scheme, and component a user might enable — usually 5–10× more than any single site uses.
  • Third-party widgets.Embeds, chat widgets, and consent banners load their own stylesheets, often containing rules for features you don't use.
  • Historical accumulation. Old features get redesigned; the new CSS gets added; the old CSS rarely gets deleted. After two or three years, large fractions of the stylesheet are dead.

Worked example: framework cost vs. component CSS

Loading a full CSS framework on a page that uses ten components is usually 5–10× more CSS than necessary:

/* Bad: imports the full framework (~200 KB) */
@import 'bootstrap/dist/css/bootstrap.css';

/* Good: import only the partials you actually use */
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/mixins';
@import 'bootstrap/scss/grid';
@import 'bootstrap/scss/buttons';
@import 'bootstrap/scss/forms';
/* ...only the components your design uses */

On a typical site this drops CSS from ~200 KB to ~30–50 KB without any visual change.

How to Measure Unused CSS

Like unused JavaScript, unused CSS shows up in synthetic audits (which scripts are bloated) and in field metrics (which pages actually feel it).

  • Greadme deep scan— flags every stylesheet with > 20 KB or > 50% unused bytes, names the file, and pairs each finding with an AI-generated fix or a one-click GitHub PR.
  • Greadme crawler scan — measures unused CSS across every indexable page, so you can see which templates carry the worst bloat.
  • Greadme AI visibility analyzer — checks whether render-blocking CSS is hurting the CWV scores AI search engines weigh as a citation signal.
  • Chrome DevTools → Coverage tab— open DevTools, run "Show Coverage", reload the page. The Coverage panel shows used vs. unused bytes for every CSS file, with the unused portion highlighted in red. This is the fastest way to confirm a specific file's waste.
  • Google Search Console → Core Web Vitals report— shows real-user LCP at the 75th percentile. If that's failing, render-blocking CSS is usually a factor.
  • web.dev measure — runs a synthetic audit that lists every stylesheet with significant unused bytes, with savings in KB.

8 Proven Ways to Eliminate Unused CSS

1. Run PurgeCSS in Your Build

PurgeCSS scans your HTML, JSX, and template files, then strips every selector that doesn't appear. It plugs into PostCSS and works with every major bundler.

// postcss.config.js
const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = {
  plugins: [
    purgecss({
      content: [
        './src/**/*.{html,js,jsx,ts,tsx}',
        './public/index.html',
      ],
      safelist: [
        'active',
        'show',
        /^modal-/,   // keep dynamically-added modal-* classes
      ],
    }),
  ],
};

Typical reduction on a framework-based site: 70–90%.

2. Use Tailwind in JIT Mode

Tailwind's JIT compiler generates only the utility classes your markup actually uses. A site that imports the full Tailwind catalog (~3 MB uncompressed) ships ~10–30 KB after JIT.

3. Adopt CSS Modules or Scoped Component Styles

CSS Modules, styled-components, vanilla-extract, and similar systems tie styles to the components that use them. When the component isn't imported, the CSS isn't shipped.

// Button.module.css ships only when Button is imported
import styles from './Button.module.css';

export default function Button({ children }) {
  return <button className={styles.button}>{children}</button>;
}

4. Split CSS Per Route

Frameworks like Next.js, Nuxt, SvelteKit, and Astro automatically split CSS per route — homepage visitors don't download the checkout's styles. If you're hand-rolling, configure your bundler to emit one CSS file per entry point.

5. Extract Critical CSS and Lazy-Load the Rest

Inline the styles needed for the above-the-fold content directly in <head>, then load the full stylesheet asynchronously. This unblocks first paint without making the rest of the page unstyled.

<head>
  <!-- Inline critical CSS for above-the-fold content -->
  <style>
    body { margin: 0; font-family: system-ui; }
    .hero { padding: 4rem 1rem; }
    /* ...just the rules that affect the first viewport */
  </style>

  <!-- Load the rest without blocking render -->
  <link rel="preload" href="/styles/full.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles/full.css"></noscript>
</head>

6. Drop Unused Frameworks Entirely

If you're using 5% of a CSS framework, write those 5% as plain CSS. Modern CSS handles flexbox, grid, custom properties, and container queries natively — most framework features are no longer needed.

7. Audit Third-Party Stylesheets

Embeds and widgets ship their own CSS. Either inline only the styles you need, or load the widget's stylesheet asynchronously so it doesn't block first paint.

8. Use Chrome DevTools Coverage to Find Quick Wins

The fastest way to identify dead rules: open Chrome DevTools, run "Show Coverage" from the command menu, reload, and sort by unused bytes. The top three files in that list usually account for 80% of your total unused CSS.

Common Unused-CSS Problems and Fixes

Problem: Full CSS Framework Loaded for a Few Components

What's happening: The site imports the entire framework bundle (~200 KB) but uses 10–20 components. 70%+ of the rules never apply.

Fix: Import the framework's individual partials instead of the full bundle, or run PurgeCSS in production. Typical saving: 100–150 KB.

Problem: One Stylesheet Linked From Every Route

What's happening: A single main.css contains styles for the entire site and loads on every page, even pages that need 5% of it.

Fix: Switch to a framework that splits CSS per route automatically (Next.js, Nuxt, SvelteKit, Astro), or configure your bundler to emit one CSS file per entry point.

Problem: PurgeCSS Strips Dynamically-Added Classes

What's happening: A class added by JavaScript at runtime (e.g. modal-open, is-active) doesn't appear in static templates, so PurgeCSS strips it. The class works in dev but breaks in production.

Fix: Add a safelist to the PurgeCSS config — either explicit class names or regex patterns like /^modal-/. Always test the production build of every interactive flow before shipping.

Problem: CMS Theme Bloat

What's happening: The theme ships CSS for every layout, color scheme, and widget the user could enable, plus styles for editor variants used only inside the admin UI.

Fix: Most platforms expose a CSS minifier or tree-shaking option in their performance settings — turn it on. For deeper savings, run PurgeCSS against the rendered HTML at build or deploy time.

Unused CSS Thresholds at a Glance

Synthetic audits use the same threshold model as unused JS: bytes wasted plus percentage wasted.

StatusUnused bytesUnused %What it means
Good< 20 KB< 30%Stylesheet is reasonably purged; further gains aren't worth the effort.
Needs improvement20–50 KB30–60%Likely a CSS framework imported in full, or a site-wide stylesheet on every route.
Poor> 50 KB> 60%Audits flag this; FCP and LCP are measurably delayed on mobile. Run PurgeCSS or split per route.

Industry analysis (HTTP Archive Web Almanac) shows the median page sits firmly in the "Poor" band — most sites ship 60–70% unused CSS at p50, more than any other resource type.

FAQ

What counts as "unused" CSS?

Any selector or rule that the browser parses into the CSSOM but that doesn't match a single element on the rendered page. Synthetic audits measure this by walking the DOM and checking each rule against every element. A media query that never triggers, a class for a component you don't use, or a hover state for a button that isn't on this page all count as unused.

How much unused CSS is too much?

Audits flag a stylesheet when unused bytes exceed 20 KB or 50%. In practice the user pain becomes visible (a measurable FCP regression on mobile) once unused CSS exceeds ~50 KB.

Why do most sites ship so much unused CSS?

Three structural reasons: most sites import a full CSS framework instead of just the partials they use, most sites link a single site-wide stylesheet on every route, and most CMS themes ship rules for every variant the user could enable. None of these are bugs — they're defaults that prioritize developer convenience over byte size.

Will PurgeCSS break my site?

It can, if you have classes that are added by JavaScript at runtime and don't appear in your static templates. Always configure a safelist for dynamically-added classes (regex patterns work well: /^modal-/, /^is-/) and test the production build of every interactive flow before shipping.

Is critical CSS still worth the effort?

Yes — critical CSS is one of the highest-leverage FCP fixes available. Inlining the rules needed for above-the-fold content removes a render-blocking network round trip entirely. The trade-off is build complexity, which is why route-based CSS splitting (which most frameworks do automatically) is often a better default for sites that don't need the last 100 ms.

Does unused CSS affect AI search engines like ChatGPT and Perplexity?

Indirectly. AI search systems most often surface pages that already rank well in traditional search — and Core Web Vitals are part of that ranking signal. A bloated stylesheet pushes LCP above the 2.5-second threshold, hurts your rankings, and reduces your odds of being chosen as a citation by Google AI Overviews or generative answer engines.

How do I find unused CSS without instrumenting the browser myself?

Run a Greadme deep scan — it surfaces every stylesheet with significant unused bytes and pairs each finding with an AI-generated fix. For a manual check, Chrome DevTools' Coverage tab shows used vs. unused bytes per file. Google Search Console's Core Web Vitals report tells you whether real users are feeling the FCP/LCP delay caused by render-blocking CSS.

Conclusion

Unused CSS is the largest waste category on the web — the median page ships 60–70% rules that never match anything. Because CSS is render-blocking, every byte sits directly on the critical path to first paint. A 50 KB purge is a 150 ms FCP improvement on mobile, with no visual change.

The fixes are mature and low-risk: PurgeCSS for traditional codebases, Tailwind's JIT mode for utility-first sites, CSS Modules or scoped styles for component-based apps, and route-based CSS splitting for everyone. Start with a Greadme deep scan to find the stylesheets that cross the 20 KB / 50% threshold, then fix them in order of bytes saved.