CSS cleanup that keeps the layout

Less CSS — the same site

Removes duplicate and unused rules, handles the cascade, CSS variables and inheritance correctly — including styles inherited from body and ancestors.

  • No signup
  • Cascade & specificity
  • Inheritance from body
3cleaning modes
768correctness tests
0third-party APIs
styles.css
A CSS file in the editor: the a.fs-link rule with position, padding, color and transition
A real stylesheet fragment: the cascade, var() and nested selectors

Tools

Tools

Three modes for three jobs: clean a file, purge a page, or take the styles of a single block.

Deduplicate & clean CSS

A big CSS file: exact duplicates, empty rules, repeated properties and overriding rules are merged or removed.

Deduplicate

Keep only the CSS used by the HTML

Paste the page HTML and CSS — only selectors that actually match something in that markup are kept.

Filter
best fit

Extract CSS for a block

Three fields: full page HTML, full page CSS and the block selector. Keeps the block styles, everything inside it, inherited styles of ancestors (including body/html) and CSS variables.

Block export

Why it is safe

Six things the engine watches over

01

The cascade stays intact

The winning declaration is chosen by importance, specificity and source order. Rules are emitted in their original order, so overrides between equal selectors never break.

02

Inheritance from html and body

Colour, font, line-height, letter-spacing, visibility and SVG fill/stroke come from every ancestor of the block, so nothing loses its look after export.

03

CSS variables in full

Both the rules using var(--x) and the rules declaring --x are kept transitively: an --a: var(--b) chain leaves in full, even when declared on another element.

04

Media queries and @supports

@media, @supports, @layer and @container are filtered recursively and keep their wrapper. A declaration inside @media may not delete the same property from a plain rule.

05

States are never lost

:hover, :focus, ::before and :has() cannot be resolved statically, so they are always kept: losing a hover state is worse than keeping a spare rule.

06

A report, not a black box

Rules before and after, dropped selectors, kept inherited styles and variables, a diff of removed lines — all visible before you copy anything.

cascade.css
A dark editor screen with CSS rules and line numbers

Render tests, not eyeballing

For every element we compute the winning value of every property, before and after cleaning. If a single value differs, the test fails.

  • A dead selector is removed from the base rule and from every media block
  • Empty @media and @supports blocks are dropped entirely
  • @keyframes and @font-face survive only when something references them
  • Idempotent: a second pass changes nothing

Use cases

What people bring here

Landing pages

One screen, one stylesheet instead of 180 KB of framework.

Redesign and component moves

Pull a widget out of a legacy project with its ancestors and variables.

Micro-frontends

A team gets only its own rules, not other teams headers and footers.

Page weight audits

Bootstrap-era leftovers, duplicates and temporary patches.

CMS and page builders

Export the styles of one page instead of the global site CSS.

Libraries and UI kits

Refactors leave dead CSS behind; the report shows it and it goes away.

Moving to a new framework

We port styles only for the screens still on the old markup. The rest never reaches the new project.

Reviewing someone else's markup

Shows which rules the page actually uses and which were written for nothing. Useful for teaching and for code review.

A block leaves with its ancestors

Type a selector in the target field or paste the markup of the block itself and we locate it in the page.

  • Selector: .card, #main, .sidebar__item
  • Or markup: paste the block HTML and get its styles
  • Chips built from the selectors in your HTML
  • If the block is not in the markup the report says so instead of cutting silently
index.html
Page markup in a code editor

Before and after

A typical case: a framework page

Averages: the real saving depends on the markup, but dead rules live in almost every project.

Before cleaning

180 KB
  • 1,240 rules, roughly 300 exact duplicates
  • Dead selectors of removed components
  • Overridden declarations that never apply anyway
  • Empty blocks and repeated @import statements

After cleaning

62 KB
  • 610 rules, zero duplicates
  • Every element renders exactly as before
  • Media queries, variables and states are all there
  • The report lists exactly what was removed

How it works

How it works

  1. Paste the CSS (or the page HTML + CSS) into the fields.
  2. Pick a mode: deduplication, filter by HTML or block export.
  3. Check the report: what was removed, which inherited styles and variables were kept.
  4. Copy or download the ready .css.

The same engine over a JSON API

The web UI is free and unlimited. Automation gets a key with a balance: $0.001–$0.003 per request, top up by card or crypto.

# экспорт одного блока по API
curl -X POST https://css-cleaner.ru/api/v1/block \
  -H "X-Api-Key: $CSSCLEANER_KEY" \
  -d html=@page.html -d css=@site.css \
  -d target=.card \
  | jq -r .css > card.css

Details

Why it is correct

Cascade & specificity

The winning declaration is chosen by importance, specificity and source order; overriding rules are merged safely.

Inheritance from body

Inherited properties from html, body and any ancestor of the block are kept, so text never loses its font or colour.

CSS variables

Rules using var() and every declaration of the variables they need are kept transitively, including --x: var(--y).

Media queries

@media, @supports and @layer are filtered recursively and keep their structure and rule order.

Questions

FAQ

What are duplicate CSS rules and how are they removed safely?

A duplicate is a rule with the same selector and declaration set; exact copies are removed with no visual change. Overriding rules are merged only when no rule of equal specificity with a conflicting property sits between them, because source order decides the winner there.

How does the tool know which rules actually render?

Selectors are translated to XPath and matched against the page DOM. For every element the cascade winner is computed from importance, specificity and source order; declarations that never win are reported as not rendered.

Why keep inherited styles from body and ancestors?

color, font-family, font-size, line-height, letter-spacing, text-align, visibility, fill/stroke and custom properties are inherited. Exporting only the block's own rules would lose the font and color, so all ancestor rules — including html and body — are kept.

How are CSS variables handled?

Rules declaring custom properties (:root { --x: … }) and every rule using var() are kept, and variables declared on ancestors of the block are pulled in automatically.

Can I get the CSS for a single block of a page?

Yes: paste the full page HTML and CSS and give the block selector. You get the block's rules, everything inside it, inherited ancestor styles and the variables.

Is the API paid?

The web UI is free and unlimited. The JSON API is billed per key balance from $0.001 per request, paid by card via YooKassa or with crypto.

Try it on your own CSS

Your file goes nowhere: processing happens in our own process, nothing is stored or shared.