DEV Community

Cover image for How to remove unused CSS and shrink your stylesheet
Skojio Community
Skojio Community

Posted on • Originally published at skojio.com

How to remove unused CSS and shrink your stylesheet

A fresh Bootstrap build is roughly 200KB of CSS. A typical site uses maybe a tenth of it. The other 90% ships to every visitor, parses on every page load, and lingers in your bundle forever — styling components you deleted two redesigns ago.

Unused CSS is the quiet tax on your Core Web Vitals: bytes downloaded, parsed, and matched against the DOM for no reason. The good news is that most of it is findable in the browser you already have open, and removable without rewriting a line of markup. If you'd rather skip the diagnosis and just shrink the file you ship, the Skojio CSS cleaner strips comments, duplicate properties, and whitespace from any stylesheet in one paste — no upload, no build step.

  • Unused CSS comes in two forms: dead selectors (whole rules nothing matches) and redundant bytes inside the rules you keep.
  • The Chrome DevTools Coverage panel shows exactly how much of each stylesheet goes unused on a given page.
  • PurgeCSS removes dead selectors during a build — but it will strip classes your JavaScript adds at runtime unless you safelist them.
  • Minifying never deletes a rule; it only removes the bytes around the rules you keep.
  • You can shrink the stylesheet you ship — comments, duplicate properties, whitespace — entirely in your browser, no pipeline required.

Why unused CSS piles up in production

CSS only ever grows. Every framework you pull in, every component you ship, every A/B test you forget to clean up adds rules — and nothing in the normal workflow ever takes them back out.

Three habits do most of the damage:

  • Frameworks shipped whole. Bootstrap, Bulma, or an old Tailwind build without a configured content array all bundle thousands of utility classes you will never reference.
  • Components that outlive their markup. You delete the promo-banner component but leave its 40 lines of CSS behind, because nothing breaks when you don't.
  • Copy-pasted resets and vendor blocks. The same normalise rules, pasted into three stylesheets, then concatenated and minified together into one fat file.

It helps to split the waste into two kinds, because they have completely different fixes. One is a diagnosis problem — you have to work out which rules nothing uses. The other is a cleanup problem — the rules you keep are simply written with more bytes than they need.

src="/blog/figures/css-two-kinds-of-waste.jpg"
alt="A diagram splitting CSS waste into two rows: dead selectors, fixed with DevTools Coverage and PurgeCSS, and redundant bytes such as comments and duplicate properties, fixed by minifying"
caption="Dead selectors need a diagnosis tool; redundant bytes just need cleaning — and only the first needs a build step."
/>

Get the categories straight and the rest of the job is mechanical. Mixing them up is how people either ship a bloated file they think is clean, or delete a rule that turns out to matter.

How to find unused CSS in the Chrome DevTools Coverage panel

The browser ships the diagnosis tool. In Chrome or Edge, open DevTools, press Cmd+Shift+P (Ctrl+Shift+P on Windows) and run Show Coverage. Click the reload icon in the Coverage tab and interact with the page while it records.

Each stylesheet then gets a red-and-green bar. Green is bytes that matched something; red is bytes that never did. Click a file and the source view highlights the unused lines directly, so you can see which selectors are dead rather than just a percentage.


Coverage is per-page and per-interaction. It only marks a rule "used" if you actually triggered it — so open the modal, hover the dropdown, and visit the checkout before you trust the numbers. A rule that looks dead on the homepage may be the only thing styling a page you didn't click.

This is the right tool for measuring the problem, not fixing it. Coverage won't rewrite your files, and deleting by hand from a 5,000-line stylesheet is how regressions happen. Treat the panel as the audit that tells you whether the waste is worth chasing — if a stylesheet is 80% red, it is. Once you've confirmed there are real dead rules to remove, automate the removal rather than hand-editing.

Remove unused CSS in a build with PurgeCSS (and where it falls short)

The standard way to delete dead selectors automatically is PurgeCSS. It scans your markup and scripts for the class names and IDs you actually reference, then drops every rule whose selector never appears.

src="/blog/figures/css-purge-terminal.jpg"
alt="A terminal running purgecss against a 248KB stylesheet, which prints a new 31KB file after removing the dead rules"
caption="PurgeCSS scans your content for referenced selectors and rewrites the stylesheet without the dead rules."
/>

Run it against your built output and a Bootstrap-sized file routinely drops by 80–90%. Tailwind does the same thing natively now — its content array is a built-in purge step — which is why a modern Tailwind build is small even though the framework defines millions of possible utilities.

The catch is everything PurgeCSS can't see. It matches selectors as literal strings in your source, so any class name your code builds at runtime is invisible to it.


PurgeCSS strips classes added by JavaScript unless you safelist them. A line like el.classList.add('is-' + state) produces class names that appear nowhere in your source as literal text, so the matching rules get purged and the component renders unstyled in production — while looking perfect in dev, where you didn't run the purge. Add a safelist for any dynamically composed class before you trust the output.

So PurgeCSS handles dead selectors well, with a safelist as the price of admission. What it deliberately does not touch is the second kind of waste — the bytes inside the rules you're keeping.


Dead selectors are a diagnosis problem; redundant bytes are a cleanup problem — and only one of them needs a build step.

Shrink the CSS you keep — without a build pipeline

After the dead rules are gone, the file you ship is still padded with bytes that do nothing: developer comments, blank lines, four-space indentation, and duplicate declarations where the same property is set twice in one block. Minifying removes all of it without changing a single thing the browser renders.

You don't need a bundler to do this for a one-off stylesheet, a vendor file, or a snippet a designer handed you. Paste it into a client-side cleaner and get the minified version straight back. Skojio's CSS cleaner strips comments, collapses whitespace, removes duplicate properties within a block, and can sort declarations for a consistent diff — all in the browser, so the stylesheet never leaves your machine.

It won't hunt down dead selectors — that's the Coverage-and-PurgeCSS job above, because only your markup knows which rules are truly unused. But for squeezing the redundancy out of the CSS you've decided to keep, pasting the file into the cleaner is faster than wiring up a build for a file you'll process once, and it typically shaves 30–70% off the size before compression.

Recap

Kind of waste How to remove it
Dead selectors (nothing matches) Find with DevTools Coverage, remove with PurgeCSS
Classes added by JavaScript at runtime Safelist them before purging
Comments, whitespace, duplicate properties Minify and clean the stylesheet
A one-off file with no build set up Paste into a client-side CSS cleaner

Find the dead rules in Coverage, prune them in your build, then run the file through the cleaner to squeeze out the bytes that are left. Most stylesheets come out a third smaller before you've touched a line of markup — and your visitors stop downloading two redesigns' worth of CSS they'll never see.

Top comments (0)