What Are Unused CSS Rules? Complete Guide (2026)
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.csslinked 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.
| Status | Unused bytes | Unused % | What it means |
|---|---|---|---|
| Good | < 20 KB | < 30% | Stylesheet is reasonably purged; further gains aren't worth the effort. |
| Needs improvement | 20–50 KB | 30–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.
