clean-css

A battle-tested CSS minifier for Node.js and the browser with three tunable optimization levels.

Library
npm
v5.3.3
4,199stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
43/100Fair
Development Activity0
Maintenance0
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
66/100Good
Architecture80
Code Quality65
Innovation72
Learning Curve45

clean-css is a CSS minifier and optimizer for Node.js and modern browsers, built to safely shrink stylesheets across three progressively more aggressive optimization levels — from lossless whitespace/comment removal up to cross-rule restructuring like merging adjacent selectors and removing duplicate or unused at-rules. It exposes a small constructor API (new CleanCSS(options).minify(input)) with support for callbacks, promises, source maps, remote @import inlining, and a plugin hook for custom level-1 transforms, making it straightforward to drop into build tools and asset pipelines.

Now in maintenance mode by its original author, the library remains one of the most widely used CSS minifiers in the npm ecosystem, still depended on by countless bundler plugins and static site generators despite the project itself receiving only occasional bugfix releases.

What You Get

  • Three optimization levels - level 0 does no restructuring, level 1 performs safe per-declaration cleanup (whitespace, redundant values, selector sorting), and level 2 restructures across rules (merging adjacent/non-adjacent selectors, removing duplicates and unused at-rules).
  • Configurable compatibility modes - built-in ie10+/ie9/ie8/ie7 compatibility presets, or a fully custom compatibility object, control which CSS features are safe to emit for target browsers.
  • Source map support - optional input source map consumption and output source map generation via the sourceMap option.
  • Remote and local @import inlining - configurable inline targets, a custom fetch handler, and request/timeout options for pulling in remote stylesheets during optimization.
  • Plugin hook - a plugins option lets you register custom level-1 transform functions without forking the library.
  • Promise and callback interfaces - minify() supports the classic Node callback style or a Promise-returning mode via returnPromise.

Common Use Cases

  • Build tool integration - CSS-processing steps in Webpack/Gulp/Grunt plugins and other bundler pipelines that need dependable, well-established minification.
  • Static site and framework asset pipelines - shrinking generated stylesheets as part of a production build step.
  • CSS asset optimization in CI - running clean-css as a standalone build script (or via the separate clean-css-cli package) to minify CSS artifacts before deployment.
  • Legacy browser support - using compatibility presets (e.g. ie9) to safely minify CSS that must still work in older Internet Explorer versions.

Under The Hood

Architecture A CleanCSS constructor in lib/clean.js normalizes constructor options through dedicated modules under lib/options/ (compatibility.js, format.js, inline.js, optimization-level.js, plugins.js, rebase.js, rebase-to.js, fetch.js, inline-request.js, inline-timeout.js), each responsible for validating and defaulting one option. Calling minify() runs a layered pipeline: readSources under lib/reader/ parses the input source(s) — handling batch mode and optional remote @import inlining via the fetch/inlineRequest/inlineTimeout options — a tokenizer builds an internal token tree, and level0Optimize/level1Optimize/level2Optimize under lib/optimizer/ progressively rewrite that tree (level 1 covers safe per-declaration property/value optimizers and selector sorting; level 2 covers cross-rule restructuring such as merge-adjacent, merge-non-adjacent-by-selector/body, remove-duplicates, and specificity/overlap-aware rule merging). Finally lib/writer/simple.js or lib/writer/source-maps.js serializes the result, consulting inputSourceMapTracker when source maps are requested. The design is modular and clearly layered (read → tokenize → optimize levels 0-2 → write), with each optimization level independently swappable; the shared token tree shape flowing between tokenizer, optimizer, and writer is the one abstraction the rest of the codebase quietly depends on.

Tech Stack Plain JavaScript targeting Node.js 10+, with a single required runtime dependency (source-map ~0.6.0) for source map generation. Development tooling includes browserify plus uglify-js to build a standalone browser bundle, ESLint (airbnb-base/legacy) for linting, and the legacy vows BDD framework paired with Node’s built-in assert module for testing, with nock mocking HTTP for remote @import tests and http-proxy/server-destroy supporting integration test servers. There is no TypeScript and no build step for the library itself — it ships plain CommonJS straight from lib/. CI runs via GitHub Actions (a dedicated test workflow plus scheduled CodeQL security scanning), with a .travis.yml retained from an earlier era. Since v4.0 the CLI was deliberately split out into a separate clean-css-cli package, keeping this package a pure programmatic API.

Code Quality The test/ directory holds around fifty JavaScript files organized by module (optimizer, reader, tokenizer, utils) using vows’ addBatch/topic pattern, plus integration, module-usage, batch, and source-map end-to-end suites. There is no TypeScript or type annotations anywhere — the codebase relies on dynamic JS with dedicated option-normalization functions (e.g. compatibilityFrom, formatFrom) that coerce and default constructor input defensively rather than throwing. Optimization errors and warnings are collected onto the output object’s errors/warnings arrays rather than always being thrown, a deliberate but easy-to-miss API contract that callers must check explicitly. ESLint enforces an airbnb-base/legacy config with several rules relaxed (eqeqeq off, block-scoped-var off), reflecting an older, pragmatic style rather than strict modern conventions.

API Design The constructor takes a single options hash with sane defaults (ie10+ compatibility, optimization level 1), and .minify(input, callback) accepts a string, array, or hash of sources, optionally returning a Promise via returnPromise instead of forcing Node-style callbacks — giving callers a choice of sync-feeling, callback, or promise usage. The plugins option lets consumers inject custom level-1 transform functions without forking the library, and fetch/inlineRequest/inlineTimeout expose fine-grained control over remote @import handling that many minifiers hide entirely. Documentation is unusually thorough for a utility library — a single 40KB+ README with a full table of contents, an explicit changelog of breaking changes per major version, and an FAQ covering common integration questions (multi-file processing, source maps, custom rounding precision). Getting started requires minimal boilerplate (new CleanCSS().minify(css)), though the breadth of the options surface — a dozen-plus top-level options, several with nested sub-options like compatibility — means non-trivial customization requires reading the docs closely, with no runtime validation errors to guide misconfiguration beyond silent defaulting.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search