Developer 🔒 Runs in your browser — no data sent to servers

Unused CSS Remover

Keep only the CSS a page uses, from a Chrome Coverage export or from the HTML and the stylesheet. Everything runs in your browser.

Coverage export from Chrome DevTools
↑
Drop the coverage JSON file here, or click to choose it
In Chrome: DevTools, then ⋮, More tools, Coverage. Reload the page, use it (open the menus, resize the window), then press Export.
Used CSS
Everything runs in your browser; nothing is uploaded.

Unused CSS Remover - Keep Only the CSS a Page Uses

Most sites ship far more CSS than any page needs. A theme, a page builder or a framework brings styles for every widget it might ever show, and the browser has to download and parse all of it before it can paint. This tool cuts a stylesheet down to the rules a page actually uses, in two ways: from the Coverage export that Chrome DevTools records, or from the page's HTML and the CSS itself.

Both run entirely in your browser. The stylesheet, the HTML and the coverage file are read on your own machine and never uploaded, which matters when the CSS belongs to a client's unreleased site.

Two ways in

ModeWhat you give itWhat it knowsBest for
Coverage JSONThe file exported from the Coverage panel in Chrome DevToolsExactly which rules the browser applied while you used the pageThemes and page builders whose classes you cannot predict
HTML + CSSThe page's HTML and the stylesheetWhich selectors match an element in that HTMLStatic pages and templates you control

How to export coverage from Chrome

  1. Open the page in Chrome and open DevTools.
  2. Open the menu with the three dots, then More tools, then Coverage.
  3. Press the reload button in the Coverage panel so the recording starts with the page load.
  4. Use the page the way a visitor would: open the menus, the accordions and the pop-ups, and resize the window to the widths you support.
  5. Press Export and save the JSON file, then drop it on the Coverage JSON tab and press Extract used CSS.

The export lists every file the page loaded, JavaScript included. The tool keeps the stylesheets, reads the inline <style> blocks out of HTML documents, and skips the rest, telling you how many entries it skipped. The same format comes out of page.coverage.stopCSSCoverage() in Puppeteer, so an automated recording works too.

Why rules go back inside their @media

Chrome records coverage one style rule at a time. For a rule inside @media (max-width: 768px) { ... } it records the rule, not the @media line around it. A tool that simply copies the recorded ranges therefore produces the mobile rule on its own, and it starts applying at every screen width. This tool reads the whole stylesheet into a tree first, so each kept rule is written back inside its own @media, @supports, @container or @layer block, and a block with nothing left in it disappears.

Some at-rules are always kept, because other rules depend on them rather than the page matching them: @font-face, @keyframes, @import, @charset, @property and the @layer order statement. Kept rules are copied as they were written, so comments inside them, formatting and vendor hacks stay exactly as in the original.

The HTML + CSS mode

Paste the page's HTML and the stylesheet, or drop both files. The tool parses the HTML the way the browser would, without running any script, and keeps a rule if any selector in it matches an element. State and pseudo-element parts such as :hover, :focus and ::before are ignored for the test, so .menu a:hover stays when the page has a .menu a. A selector the browser cannot parse is kept rather than guessed at, and @keyframes survive only while a kept rule or an inline style still names the animation.

The weak spot is JavaScript. A class that a script adds later, such as is-open on a menu or active on a slide, is not in the HTML you pasted, so its rules look unused. List those names in Always keep, separated by spaces or commas; ids work with or without #, and a pattern between slashes, such as /^elementor-/, keeps every class that matches it. If several pages share one stylesheet, paste all of their HTML one after another.

What the numbers mean

After each run the tool shows how much it kept, how many rules survived and, in coverage mode, a line per stylesheet. In the HTML + CSS mode, Removed selectors lists what was dropped, which is the fastest way to spot a class that should have been on the safelist. Keep the original file: the result is a new stylesheet, and the honest test is to load the page with it and look.

Limits worth knowing

  • Coverage only knows what happened while it was recording. A hover state you never triggered, a menu you never opened or a width you never tried counts as unused.
  • One page's coverage says nothing about the other pages that load the same file. Run the tool on each main template, or keep a shared base stylesheet whole.
  • Styles added by JavaScript at runtime, through style attributes or injected sheets, are outside what either mode can see.
  • Stylesheets from other sites, such as fonts or widget CSS on a CDN, appear in the export too; keep or drop them on purpose.

Where it helps most

  • Pages built with Elementor, Divi or another page builder, which load every widget's CSS on every page. The guide to turning an Elementor page into plain HTML and CSS walks through the whole job.
  • Themes and frameworks such as Bootstrap, where a landing page uses a fraction of the classes.
  • Inlining critical CSS or building an email template, where every byte of style has a cost.
  • Auditing an old stylesheet before a redesign, to see which rules any page still touches.

Doing it on the command line

For a build step, PurgeCSS does the HTML + CSS job from a script. It reads the content files and the stylesheets and writes the cleaned CSS to a folder:

npx purgecss --css css/style.css --content index.html --output dist/css/

Puppeteer records the same coverage the Coverage panel does, and its output can be saved as JSON and dropped on this tool:

await page.coverage.startCSSCoverage();
await page.goto('https://example.com');
const coverage = await page.coverage.stopCSSCoverage();
fs.writeFileSync('coverage.json', JSON.stringify(coverage));

Related tools

A coverage export is JSON, so the JSON formatter is the quickest way to look inside one. Build and test the patterns for Always keep with the regex tester, and compress the images the lighter page still carries with the image converter.

Guides