clean-css 是一个高性能的 CSS 优化器和压缩器,专注于通过算法移除冗余代码、优化选择器并压缩体积,通常作为独立工具或构建插件的核心引擎使用。cssnano 则是基于 PostCSS 生态系统的优化器集合,它本身是一个包装器,调用多个小型 PostCSS 插件(包括 cssnano-preset-default 中集成的压缩逻辑)来处理 CSS,强调模块化和与 PostCSS 工作流的深度集成。gulp-clean-css 是 clean-css 的 Gulp 插件封装,专为 Gulp 流式构建设计;postcss-clean 是将 clean-css 引擎桥接到 PostCSS 流程的插件;而 postcss-minify 通常指代早期的或特定实现的 PostCSS 压缩插件,但在现代架构中,其功能大多已被 cssnano 的默认预设所覆盖或取代。这些工具的核心目标都是减小 CSS 文件体积以提升加载性能,但它们在集成方式、依赖生态及维护状态上存在显著差异。
在前端工程化架构中,CSS 压缩不仅仅是去掉空格,它涉及选择器重组、属性合并、颜色值优化以及移除未使用的代码。clean-css 和 cssnano 是目前 JavaScript 生态中最主流的两大解决方案,而 gulp-clean-css、postcss-clean 和 postcss-minify 则是它们在不同构建场景下的封装或历史产物。本文将从架构原理、集成方式、配置灵活性和维护状态四个维度,深入剖析这些工具的差异,帮助你在复杂的构建系统中做出正确选型。
clean-css 是一个独立的、高度优化的 CSS 处理引擎。它不依赖 PostCSS,拥有自己的解析器和优化器。这意味着它可以运行在任何 JavaScript 环境中,从 Node.js 脚本到浏览器端(虽然较少见),并且被广泛集成到 Webpack、Vite 等现代打包器的底层插件中。
// clean-css: 独立使用示例
const CleanCSS = require('clean-css');
const source = 'body { color: #fff; background: #fff; }';
const output = new CleanCSS({ level: 2 }).minify(source);
console.log(output.styles);
// 输出: body{color:#fff;background:#fff}
// 注意:level 2 会尝试更激进的优化,如属性合并
cssnano 本质上是一个 PostCSS 插件的集合管理器。它默认不执行具体的压缩逻辑,而是加载一系列小型的 PostCSS 插件(如 postcss-discard-comments, postcss-merge-rules 等)来协同工作。这种架构使其能够无缝嵌入任何 PostCSS 流程,并利用 PostCSS 的 AST(抽象语法树)进行精确操作。
// cssnano: 作为 PostCSS 插件使用
const postcss = require('postcss');
const cssnano = require('cssnano');
const source = 'body { color: #fff; background: #fff; }';
postcss([cssnano])
.process(source, { from: undefined })
.then(result => {
console.log(result.css);
// 输出: body{background:#fff;color:#fff}
// cssnano 默认会按字母顺序排序属性以便进一步压缩
});
postcss-minify 是一个较老的独立 PostCSS 插件,旨在提供基础的压缩功能。然而,随着 cssnano 的成熟,postcss-minify 的功能集显得单薄且维护滞后。在现代架构中,它几乎完全被 cssnano 的 preset-default 所取代,继续使用该包通常意味着技术栈更新不及时。
// postcss-minify: 旧式用法 (不推荐)
const postcss = require('postcss');
const minify = require('postcss-minify'); // 功能已被 cssnano 覆盖
// 此包缺乏 cssnano 的高级预设和细粒度控制
// 在现代项目中应避免使用,转而使用 cssnano
gulp-clean-css 是 clean-css 官方维护的 Gulp 插件。它将 Gulp 的 Vinyl 文件流转换为 clean-css 可处理的字符串,处理后再转回流。如果你仍在使用 Gulp 作为主要构建工具,这是最标准的选择,但它本质上只是 clean-css 的一层薄包装。
// gulp-clean-css: Gulp 流式处理
const gulp = require('gulp');
const cleanCss = require('gulp-clean-css');
gulp.task('minify-css', () => {
return gulp.src('src/*.css')
.pipe(cleanCss({ compatibility: 'ie11' })) // 直接传递 clean-css 选项
.pipe(gulp.dest('dist'));
});
postcss-clean 扮演了桥梁的角色。它允许你在 PostCSS 流程中调用 clean-css 的强大引擎。这在某些特定场景下非常有用:例如,你的团队已经建立了复杂的 PostCSS 插件链,但发现 cssnano 的某些默认行为不符合需求,而你又特别依赖 clean-css 的 Level 2 优化算法。
// postcss-clean: 在 PostCSS 中使用 clean-css 引擎
const postcss = require('postcss');
const clean = require('postcss-clean');
postcss([clean({ level: 2 })])
.process(source, { from: undefined })
.then(result => {
// 这里使用的是 clean-css 的算法,但通过 PostCSS 接口调用
console.log(result.css);
});
clean-css 的配置核心在于 level 选项(1 或 2)。Level 1 专注于安全的单文件优化(如空格移除、颜色简写);Level 2 则执行跨选择器的优化(如合并相同属性的规则块)。这种二元配置简单直接,适合需要明确控制优化风险的场景。
// clean-css: 精细控制优化级别
const CleanCSS = require('clean-css');
// Level 1: 安全,快速
const level1 = new CleanCSS({ level: 1 }).minify(source);
// Level 2: 激进,可能改变结构但体积更小
const level2 = new CleanCSS({ level: 2 }).minify(source);
// 还可以针对特定属性进行微调
const custom = new CleanCSS({
level: { 2: { mergeMedia: false } } // 禁用媒体查询合并
}).minify(source);
cssnano 通过 "Presets"(预设)提供配置。default 预设包含了一套经过验证的插件组合,适合大多数生产环境。你也可以创建自定义预设,启用或禁用特定的子插件,从而实现比 clean-css 更细粒度的控制,比如只压缩颜色但不合并选择器。
// cssnano: 使用预设和插件级配置
const cssnano = require('cssnano');
// 使用默认预设
const defaultConfig = { preset: 'default' };
// 自定义预设:禁用某些激进优化
const customConfig = {
preset: ['default', {
discardComments: { removeAll: true },
mergeLonghand: false, // 禁止合并长手属性
normalizeWhitespace: false
}]
};
postcss([cssnano(customConfig)]).process(source);
在技术选型中,包的健康状况至关重要。
clean-css: 维护活跃,是许多现代打包器(如 Webpack 5 的 css-minimizer-webpack-plugin)的默认后端。它是独立引擎的首选。cssnano: 维护极其活跃,是 PostCSS 社区的标准压缩方案。其模块化设计使其能快速适配新的 CSS 特性。gulp-clean-css: 维护状态良好,但仅限于 Gulp 生态。随着 Gulp 在新项目中份额下降,其重要性也在降低。postcss-clean: 维护频率较低。由于 cssnano 已经非常强大,该包的存在的必要性正在减弱,仅建议在有特殊算法需求时使用。postcss-minify: 强烈不建议使用。该包在功能上已被 cssnano 完全覆盖,且更新停滞。在新架构中引入它不仅增加依赖冗余,还可能带来未知的兼容性风险。// ❌ 错误示范:在新项目中使用过时的 postcss-minify
// const minify = require('postcss-minify');
// 风险:功能缺失,无新 CSS 特性支持
// ✅ 正确示范:使用 cssnano 替代
const cssnano = require('cssnano');
// 优势:持续更新,社区支持,功能全面
你的项目使用 Vite 或 Webpack 5,需要最小的 CSS 体积。
clean-css (通过官方插件间接使用) 或 cssnano (通过 PostCSS 配置)。cssnano (通过 PostCSS),而 Webpack 5 的 css-minimizer-webpack-plugin 默认使用 clean-css。两者性能相当,只需遵循框架默认配置即可,无需额外安装独立包。// vite.config.js (隐含使用 cssnano)
export default {
css: {
postcss: {
plugins: [require('autoprefixer'), require('cssnano')]
}
}
};
你正在维护一个大型老项目,构建流程完全基于 Gulp 4,暂时无法迁移。
gulp-clean-css。.pipe() 链,无需重写构建逻辑。虽然底层是 clean-css,但封装层提供了最舒适的 Gulp 开发体验。// gulpfile.js
const cleanCss = require('gulp-clean-css');
gulp.src('styles/*.css').pipe(cleanCss()).pipe(gulp.dest('dist'));
你需要在一个复杂的 PostCSS 链中,精确控制哪些步骤在压缩前执行,并且特别依赖 clean-css 的 Level 2 重组算法,而不喜欢 cssnano 的默认行为。
postcss-clean。clean-css 作为一个普通插件插入链中的任意位置,既保留了 PostCSS 的灵活性,又利用了 clean-css 的特定算法优势。// postcss.config.js
module.exports = {
plugins: [
require('postcss-import'),
require('postcss-clean')({ level: 2 }), // 精确插入点
require('autoprefixer')
]
};
| 特性 | clean-css | cssnano | gulp-clean-css | postcss-clean | postcss-minify |
|---|---|---|---|---|---|
| 核心定位 | 独立压缩引擎 | PostCSS 插件集合 | Gulp 封装 | PostCSS 桥接 | 过时插件 |
| 依赖生态 | 无 (独立) | PostCSS | Gulp + clean-css | PostCSS + clean-css | PostCSS |
| 配置方式 | level (1/2) | Presets (预设) | 透传 clean-css 选项 | 透传 clean-css 选项 | 有限选项 |
| 维护状态 | ✅ 活跃 | ✅ 非常活跃 | ⚠️ 维护中 (Gulp 专用) | ⚠️ 低频 | ❌ 不推荐/停滞 |
| 适用场景 | Webpack/Vite 底层 | PostCSS 标准流 | 遗留 Gulp 项目 | 特殊 PostCSS 需求 | 无 (请避免) |
在现代前端架构中,cssnano 和 clean-css 是唯二值得深入考量的核心库。
cssnano 是自然的选择。它的模块化设计让你可以像搭积木一样定制优化流程,且社区生态最为丰富。clean-css 的深度优化支持,那么直接使用它是最稳妥的方案。它的 Level 2 优化在处理大型遗留 CSS 文件时往往表现出色。至于 gulp-clean-css,它只是特定工具链下的产物,不应作为技术选型的核心理由;而 postcss-clean 仅在有非常具体的算法偏好冲突时才值得考虑。最重要的是,请坚决从你的 package.json 中移除 postcss-minify,它是过去时代的残留物,不再符合现代 Web 开发的标准。
最终,无论选择哪个工具,关键在于将其纳入自动化的 CI/CD 流程,并配合 Source Map 以确保压缩后的代码在调试时依然可追溯。性能优化不应以牺牲开发体验为代价。
选择 clean-css 如果你需要一个不依赖 PostCSS 的独立压缩引擎,或者你的构建流程基于 Webpack (通过 css-minimizer-webpack-plugin)、Vite 或其他非 PostCSS 原生的工具链。它在处理大型 CSS 文件时具有极高的性能表现,且 API 稳定,适合作为底层核心库直接调用或集成到自定义构建脚本中。
选择 cssnano 如果你的项目已经广泛使用 PostCSS 生态系统(如 Autoprefixer、postcss-preset-env),并且希望在一个统一的配置文件中管理所有 CSS 转换步骤。它提供了从安全压缩到激进优化的多种预设(presets),能够灵活平衡压缩率与安全性,是现代前端工程化中最通用的标准选择。
不建议在新项目中选择 postcss-minify。该包在现代 PostCSS 生态中已显得多余,因为其核心功能完全被 cssnano 覆盖,且维护活跃度较低。如果文档或旧教程中提到它,请将其替换为 cssnano,以避免潜在的兼容性问题和依赖冗余。
选择 postcss-clean 的场景非常特定:当你必须使用 clean-css 的高级优化算法(如特定级别的重组逻辑),但又强制要求该步骤必须发生在 PostCSS 插件链中时。在大多数情况下,cssnano 的默认配置已足够优秀,因此除非你有明确的算法偏好差异,否则优先推荐使用 cssnano 以获得更好的长期维护支持。
选择 gulp-clean-css 仅当你正在维护一个遗留的 Gulp 4 或更早版本的构建系统,且不希望重构整个流水线为 Webpack 或 Vite。对于新项目,不建议直接引入此包,而应优先考虑迁移到更现代的构建工具或使用 Gulp 调用原生的 clean-css 实例以保持依赖精简。
clean-css is a fast and efficient CSS optimizer for Node.js platform and any modern browser.
According to tests it is one of the best available.
Table of Contents
@imports correctly?clean-css requires Node.js 10.0+ (tested on Linux, OS X, and Windows)
npm install --save-dev clean-css
var CleanCSS = require('clean-css');
var input = 'a{font-weight:bold;}';
var options = { /* options */ };
var output = new CleanCSS(options).minify(input);
clean-css 5.3 introduces one new feature:
variableValueOptimizers option, which accepts a list of value optimizers or a list of their names, e.g. variableValueOptimizers: ['color', 'fraction'].clean-css 5.0 introduced some breaking changes:
transform callback in level-1 optimizations is removed in favor of new plugins interface;{ compatibility: 'ie10' } flag;rebase option from true to false so URLs are not rebased by default. Please note that if you set rebaseTo option it still counts as setting rebase: true to preserve some of the backward compatibility.And on the new features side of things:
format: {breaks: {afterComment: 2}} means clean-css will add two line breaks after each commentbatch option (defaults to false) is added, when set to true it will process all inputs, given either as an array or a hash, without concatenating them.clean-css 4.2 introduces the following changes / features:
process method for compatibility with optimize-css-assets-webpack-plugin;transition property optimizer;/* clean-css ignore:start */ and /* clean-css ignore:end */ comments;transform callback, see example;format: { breakWith: 'lf' } option.clean-css 4.1 introduces the following changes / features:
inline: false as an alias to inline: ['none'];multiplePseudoMerging compatibility flag controlling merging of rules with multiple pseudo classes / elements;removeEmpty flag in level 1 optimizations controlling removal of rules and nested blocks;removeEmpty flag in level 2 optimizations controlling removal of rules and nested blocks;compatibility: { selectors: { mergeLimit: <number> } } flag in compatibility settings controlling maximum number of selectors in a single rule;minify method improved signature accepting a list of hashes for a predictable traversal;selectorsSortingMethod level 1 optimization allows false or 'none' for disabling selector sorting;fetch option controlling a function for handling remote requests;font shorthand and font-* longhand optimizers;optimizeFont flag in level 1 optimizations due to new font shorthand optimizer;skipProperties flag in level 2 optimizations controlling which properties won't be optimized;animation shorthand and animation-* longhand optimizers;removeUnusedAtRules level 2 optimization controlling removal of unused @counter-style, @font-face, @keyframes, and @namespace at rules;clean-css 4.0 introduces some breaking changes:
root, relativeTo, and target options are replaced by a single rebaseTo option - this means that rebasing URLs and import inlining is much simpler but may not be (YMMV) as powerful as in 3.x;debug option is gone as stats are always provided in output object under stats property;roundingPrecision is disabled by default;roundingPrecision applies to all units now, not only px as in 3.x;processImport and processImportFrom are merged into inline option which defaults to local. Remote @import rules are NOT inlined by default anymore;inliner: { request: ..., timeout: ... } option into inlineRequest and inlineTimeout options;//fonts.googleapis.com/css?family=Domine:700, are not inlined anymore;{ compatibility: 'ie9' } flag;keepSpecialComments to specialComments;roundingPrecision and specialComments to level 1 optimizations options, see examples;mediaMerging, restructuring, semanticMerging, and shorthandCompacting to level 2 optimizations options, see examples below;shorthandCompacting option to mergeIntoShorthands;keepBreaks option is replaced with { format: 'keep-breaks' } to ease transition;sourceMap option has to be a boolean from now on - to specify an input source map pass it a 2nd argument to minify method or via a hash instead;aggressiveMerging option is removed as aggressive merging is replaced by smarter override merging.clean-css constructor accepts a hash as a parameter with the following options available:
compatibility - controls compatibility mode used; defaults to ie10+; see compatibility modes for examples;fetch - controls a function for handling remote requests; see fetch option for examples (since 4.1.0);format - controls output CSS formatting; defaults to false; see formatting options for examples;inline - controls @import inlining rules; defaults to 'local'; see inlining options for examples;inlineRequest - controls extra options for inlining remote @import rules, can be any of HTTP(S) request options;inlineTimeout - controls number of milliseconds after which inlining a remote @import fails; defaults to 5000;level - controls optimization level used; defaults to 1; see optimization levels for examples;rebase - controls URL rebasing; defaults to false;rebaseTo - controls a directory to which all URLs are rebased, most likely the directory under which the output file will live; defaults to the current directory;returnPromise - controls whether minify method returns a Promise object or not; defaults to false; see promise interface for examples;sourceMap - controls whether an output source map is built; defaults to false;sourceMapInlineSources - controls embedding sources inside a source map's sourcesContent field; defaults to false.There is a certain number of compatibility mode shortcuts, namely:
new CleanCSS({ compatibility: '*' }) (default) - Internet Explorer 10+ compatibility modenew CleanCSS({ compatibility: 'ie9' }) - Internet Explorer 9+ compatibility modenew CleanCSS({ compatibility: 'ie8' }) - Internet Explorer 8+ compatibility modenew CleanCSS({ compatibility: 'ie7' }) - Internet Explorer 7+ compatibility modeEach of these modes is an alias to a fine grained configuration, with the following options available:
new CleanCSS({
compatibility: {
colors: {
hexAlpha: false, // controls 4- and 8-character hex color support
opacity: true // controls `rgba()` / `hsla()` color support
},
properties: {
backgroundClipMerging: true, // controls background-clip merging into shorthand
backgroundOriginMerging: true, // controls background-origin merging into shorthand
backgroundSizeMerging: true, // controls background-size merging into shorthand
colors: true, // controls color optimizations
ieBangHack: false, // controls keeping IE bang hack
ieFilters: false, // controls keeping IE `filter` / `-ms-filter`
iePrefixHack: false, // controls keeping IE prefix hack
ieSuffixHack: false, // controls keeping IE suffix hack
merging: true, // controls property merging based on understandability
shorterLengthUnits: false, // controls shortening pixel units into `pc`, `pt`, or `in` units
spaceAfterClosingBrace: true, // controls keeping space after closing brace - `url() no-repeat` into `url()no-repeat`
urlQuotes: true, // controls keeping quoting inside `url()`
zeroUnits: true // controls removal of units `0` value
},
selectors: {
adjacentSpace: false, // controls extra space before `nav` element
ie7Hack: true, // controls removal of IE7 selector hacks, e.g. `*+html...`
mergeablePseudoClasses: [':active', ...], // controls a whitelist of mergeable pseudo classes
mergeablePseudoElements: ['::after', ...], // controls a whitelist of mergeable pseudo elements
mergeLimit: 8191, // controls maximum number of selectors in a single rule (since 4.1.0)
multiplePseudoMerging: true // controls merging of rules with multiple pseudo classes / elements (since 4.1.0)
},
units: {
ch: true, // controls treating `ch` as a supported unit
in: true, // controls treating `in` as a supported unit
pc: true, // controls treating `pc` as a supported unit
pt: true, // controls treating `pt` as a supported unit
rem: true, // controls treating `rem` as a supported unit
vh: true, // controls treating `vh` as a supported unit
vm: true, // controls treating `vm` as a supported unit
vmax: true, // controls treating `vmax` as a supported unit
vmin: true // controls treating `vmin` as a supported unit
}
}
})
You can also use a string when setting a compatibility mode, e.g.
new CleanCSS({
compatibility: 'ie9,-properties.merging' // sets compatibility to IE9 mode with disabled property merging
})
The fetch option accepts a function which handles remote resource fetching, e.g.
var request = require('request');
var source = '@import url(http://example.com/path/to/stylesheet.css);';
new CleanCSS({
fetch: function (uri, inlineRequest, inlineTimeout, callback) {
request(uri, function (error, response, body) {
if (error) {
callback(error, null);
} else if (response && response.statusCode != 200) {
callback(response.statusCode, null);
} else {
callback(null, body);
}
});
}
}).minify(source);
This option provides a convenient way of overriding the default fetching logic if it doesn't support a particular feature, say CONNECT proxies.
Unless given, the default loadRemoteResource logic is used.
By default output CSS is formatted without any whitespace unless a format option is given.
First of all there are two shorthands:
new CleanCSS({
format: 'beautify' // formats output in a really nice way
})
and
new CleanCSS({
format: 'keep-breaks' // formats output the default way but adds line breaks for improved readability
})
however format option also accept a fine-grained set of options:
new CleanCSS({
format: {
breaks: { // controls where to insert breaks
afterAtRule: false, // controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `false`
afterBlockBegins: false, // controls if a line break comes after a block begins; e.g. `@media`; defaults to `false`
afterBlockEnds: false, // controls if a line break comes after a block ends, defaults to `false`
afterComment: false, // controls if a line break comes after a comment; defaults to `false`
afterProperty: false, // controls if a line break comes after a property; defaults to `false`
afterRuleBegins: false, // controls if a line break comes after a rule begins; defaults to `false`
afterRuleEnds: false, // controls if a line break comes after a rule ends; defaults to `false`
beforeBlockEnds: false, // controls if a line break comes before a block ends; defaults to `false`
betweenSelectors: false // controls if a line break comes between selectors; defaults to `false`
},
breakWith: '\n', // controls the new line character, can be `'\r\n'` or `'\n'` (aliased as `'windows'` and `'unix'` or `'crlf'` and `'lf'`); defaults to system one, so former on Windows and latter on Unix
indentBy: 0, // controls number of characters to indent with; defaults to `0`
indentWith: 'space', // controls a character to indent with, can be `'space'` or `'tab'`; defaults to `'space'`
spaces: { // controls where to insert spaces
aroundSelectorRelation: false, // controls if spaces come around selector relations; e.g. `div > a`; defaults to `false`
beforeBlockBegins: false, // controls if a space comes before a block begins; e.g. `.block {`; defaults to `false`
beforeValue: false // controls if a space comes before a value; e.g. `width: 1rem`; defaults to `false`
},
wrapAt: false, // controls maximum line length; defaults to `false`
semicolonAfterLastProperty: false // controls removing trailing semicolons in rule; defaults to `false` - means remove
}
})
Also since clean-css 5.0 you can use numerical values for all line breaks, which will repeat a line break that many times, e.g:
new CleanCSS({
format: {
breaks: {
afterAtRule: 2,
afterBlockBegins: 1, // 1 is synonymous with `true`
afterBlockEnds: 2,
afterComment: 1,
afterProperty: 1,
afterRuleBegins: 1,
afterRuleEnds: 1,
beforeBlockEnds: 1,
betweenSelectors: 0 // 0 is synonymous with `false`
}
}
})
which will add nicer spacing between at rules and blocks.
inline option whitelists which @import rules will be processed, e.g.
new CleanCSS({
inline: ['local'] // default; enables local inlining only
})
new CleanCSS({
inline: ['none'] // disables all inlining
})
// introduced in clean-css 4.1.0
new CleanCSS({
inline: false // disables all inlining (alias to `['none']`)
})
new CleanCSS({
inline: ['all'] // enables all inlining, same as ['local', 'remote']
})
new CleanCSS({
inline: ['local', 'mydomain.example.com'] // enables local inlining plus given remote source
})
new CleanCSS({
inline: ['local', 'remote', '!fonts.googleapis.com'] // enables all inlining but from given remote source
})
The level option can be either 0, 1 (default), or 2, e.g.
new CleanCSS({
level: 2
})
or a fine-grained configuration given via a hash.
Please note that level 1 optimization options are generally safe while level 2 optimizations should be safe for most users.
Level 0 optimizations simply means "no optimizations". Use it when you'd like to inline imports and / or rebase URLs but skip everything else.
Level 1 optimizations (default) operate on single properties only, e.g. can remove units when not required, turn rgb colors to a shorter hex representation, remove comments, etc
Here is a full list of available options:
new CleanCSS({
level: {
1: {
cleanupCharsets: true, // controls `@charset` moving to the front of a stylesheet; defaults to `true`
normalizeUrls: true, // controls URL normalization; defaults to `true`
optimizeBackground: true, // controls `background` property optimizations; defaults to `true`
optimizeBorderRadius: true, // controls `border-radius` property optimizations; defaults to `true`
optimizeFilter: true, // controls `filter` property optimizations; defaults to `true`
optimizeFont: true, // controls `font` property optimizations; defaults to `true`
optimizeFontWeight: true, // controls `font-weight` property optimizations; defaults to `true`
optimizeOutline: true, // controls `outline` property optimizations; defaults to `true`
removeEmpty: true, // controls removing empty rules and nested blocks; defaults to `true`
removeNegativePaddings: true, // controls removing negative paddings; defaults to `true`
removeQuotes: true, // controls removing quotes when unnecessary; defaults to `true`
removeWhitespace: true, // controls removing unused whitespace; defaults to `true`
replaceMultipleZeros: true, // contols removing redundant zeros; defaults to `true`
replaceTimeUnits: true, // controls replacing time units with shorter values; defaults to `true`
replaceZeroUnits: true, // controls replacing zero values with units; defaults to `true`
roundingPrecision: false, // rounds pixel values to `N` decimal places; `false` disables rounding; defaults to `false`
selectorsSortingMethod: 'standard', // denotes selector sorting method; can be `'natural'` or `'standard'`, `'none'`, or false (the last two since 4.1.0); defaults to `'standard'`
specialComments: 'all', // denotes a number of /*! ... */ comments preserved; defaults to `all`
tidyAtRules: true, // controls at-rules (e.g. `@charset`, `@import`) optimizing; defaults to `true`
tidyBlockScopes: true, // controls block scopes (e.g. `@media`) optimizing; defaults to `true`
tidySelectors: true, // controls selectors optimizing; defaults to `true`,
variableValueOptimizers: [] // controls value optimizers which are applied to variables
}
}
});
There is an all shortcut for toggling all options at the same time, e.g.
new CleanCSS({
level: {
1: {
all: false, // set all values to `false`
tidySelectors: true // turns on optimizing selectors
}
}
});
Level 2 optimizations operate at rules or multiple properties level, e.g. can remove duplicate rules, remove properties redefined further down a stylesheet, or restructure rules by moving them around.
Please note that if level 2 optimizations are turned on then, unless explicitely disabled, level 1 optimizations are applied as well.
Here is a full list of available options:
new CleanCSS({
level: {
2: {
mergeAdjacentRules: true, // controls adjacent rules merging; defaults to true
mergeIntoShorthands: true, // controls merging properties into shorthands; defaults to true
mergeMedia: true, // controls `@media` merging; defaults to true
mergeNonAdjacentRules: true, // controls non-adjacent rule merging; defaults to true
mergeSemantically: false, // controls semantic merging; defaults to false
overrideProperties: true, // controls property overriding based on understandability; defaults to true
removeEmpty: true, // controls removing empty rules and nested blocks; defaults to `true`
reduceNonAdjacentRules: true, // controls non-adjacent rule reducing; defaults to true
removeDuplicateFontRules: true, // controls duplicate `@font-face` removing; defaults to true
removeDuplicateMediaBlocks: true, // controls duplicate `@media` removing; defaults to true
removeDuplicateRules: true, // controls duplicate rules removing; defaults to true
removeUnusedAtRules: false, // controls unused at rule removing; defaults to false (available since 4.1.0)
restructureRules: false, // controls rule restructuring; defaults to false
skipProperties: [] // controls which properties won't be optimized, defaults to `[]` which means all will be optimized (since 4.1.0)
}
}
});
There is an all shortcut for toggling all options at the same time, e.g.
new CleanCSS({
level: {
2: {
all: false, // sets all values to `false`
removeDuplicateRules: true // turns on removing duplicate rules
}
}
});
In clean-css version 5 and above you can define plugins which run alongside level 1 and level 2 optimizations, e.g.
var myPlugin = {
level1: {
property: function removeRepeatedBackgroundRepeat(_rule, property, _options) {
// So `background-repeat:no-repeat no-repeat` becomes `background-repeat:no-repeat`
if (property.name == 'background-repeat' && property.value.length == 2 && property.value[0][1] == property.value[1][1]) {
property.value.pop();
property.dirty = true;
}
}
}
}
new CleanCSS({plugins: [myPlugin]})
Search test\module-test.js for plugins or check out lib/optimizer/level-1/property-optimizers and lib/optimizer/level-1/value-optimizers for more examples.
Important: To rewrite your old transform as a plugin, check out this commit.
Once configured clean-css provides a minify method to optimize a given CSS, e.g.
var output = new CleanCSS(options).minify(source);
The output of the minify method is a hash with following fields:
console.log(output.styles); // optimized output CSS as a string
console.log(output.sourceMap); // output source map if requested with `sourceMap` option
console.log(output.errors); // a list of errors raised
console.log(output.warnings); // a list of warnings raised
console.log(output.stats.originalSize); // original content size after import inlining
console.log(output.stats.minifiedSize); // optimized content size
console.log(output.stats.timeSpent); // time spent on optimizations in milliseconds
console.log(output.stats.efficiency); // `(originalSize - minifiedSize) / originalSize`, e.g. 0.25 if size is reduced from 100 bytes to 75 bytes
Example: Minifying a CSS string:
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
a {
color: blue;
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'a{color:#00f}div{margin:5px}',
stats: {
efficiency: 0.6704545454545454,
minifiedSize: 29,
originalSize: 88,
timeSpent: 6
},
errors: [],
inlinedStylesheets: [],
warnings: []
}
The minify method also accepts an input source map, e.g.
var output = new CleanCSS(options).minify(source, inputSourceMap);
or a callback invoked when optimizations are finished, e.g.
new CleanCSS(options).minify(source, function (error, output) {
// `output` is the same as in the synchronous call above
});
To optimize a single file, without reading it first, pass a path to it to minify method as follows:
var output = new CleanCSS(options).minify(['path/to/file.css'])
(if you won't enclose the path in an array, it will be treated as a CSS source instead).
There are several ways to optimize multiple files at the same time, see How to optimize multiple files?.
If you prefer clean-css to return a Promise object then you need to explicitely ask for it, e.g.
new CleanCSS({ returnPromise: true })
.minify(source)
.then(function (output) { console.log(output.styles); })
.catch(function (error) { // deal with errors });
Clean-css has an associated command line utility that can be installed separately using npm install clean-css-cli. For more detailed information, please visit https://github.com/clean-css/clean-css-cli.
It can be done either by passing an array of paths, or, when sources are already available, a hash or an array of hashes:
new CleanCSS().minify(['path/to/file/one', 'path/to/file/two']);
new CleanCSS().minify({
'path/to/file/one': {
styles: 'contents of file one'
},
'path/to/file/two': {
styles: 'contents of file two'
}
});
new CleanCSS().minify([
{'path/to/file/one': {styles: 'contents of file one'}},
{'path/to/file/two': {styles: 'contents of file two'}}
]);
Passing an array of hashes allows you to explicitly specify the order in which the input files are concatenated. Whereas when you use a single hash the order is determined by the traversal order of object properties - available since 4.1.0.
Important note - any @import rules already present in the hash will be resolved in memory.
Since clean-css 5.0 you can, when passing an array of paths, hash, or array of hashes (see above), ask clean-css not to join styles into one output, but instead return stylesheets optimized one by one, e.g.
var output = new CleanCSS({ batch: true }).minify(['path/to/file/one', 'path/to/file/two']);
var outputOfFile1 = output['path/to/file/one'].styles // all other fields, like errors, warnings, or stats are there too
var outputOfFile2 = output['path/to/file/two'].styles
@imports correctly?In order to inline remote @import statements you need to provide a callback to minify method as fetching remote assets is an asynchronous operation, e.g.:
var source = '@import url(http://example.com/path/to/remote/styles);';
new CleanCSS({ inline: ['remote'] }).minify(source, function (error, output) {
// output.styles
});
If you don't provide a callback, then remote @imports will be left as is.
Please see plugins.
The level 1 roundingPrecision optimization option accept a string with per-unit rounding precision settings, e.g.
new CleanCSS({
level: {
1: {
roundingPrecision: 'all=3,px=5'
}
}
}).minify(source)
which sets all units rounding precision to 3 digits except px unit precision of 5 digits.
rpx units?Since rpx is a non standard unit (see #1074), it will be dropped by default as an invalid value.
However you can treat rpx units as regular ones:
new CleanCSS({
compatibility: {
customUnits: {
rpx: true
}
}
}).minify(source)
Note: available since 4.2.0.
Wrap the CSS fragment in special comments which instruct clean-css to preserve it, e.g.
.block-1 {
color: red
}
/* clean-css ignore:start */
.block-special {
color: transparent
}
/* clean-css ignore:end */
.block-2 {
margin: 0
}
Optimizing this CSS will result in the following output:
.block-1{color:red}
.block-special {
color: transparent
}
.block-2{margin:0}
Use the /*! notation instead of the standard one /*:
/*!
Important comments included in optimized output.
*/
clean-css will handle it automatically for you in the following cases:
rebaseTo is used with any of above two.To generate a source map, use sourceMap: true option, e.g.:
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
.minify(source, function (error, output) {
// access output.sourceMap for SourceMapGenerator object
// see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
});
You can also pass an input source map directly as a 2nd argument to minify method:
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
.minify(source, inputSourceMap, function (error, output) {
// access output.sourceMap to access SourceMapGenerator object
// see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
});
or even multiple input source maps at once:
new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory }).minify({
'path/to/source/1': {
styles: '...styles...',
sourceMap: '...source-map...'
},
'path/to/source/2': {
styles: '...styles...',
sourceMap: '...source-map...'
}
}, function (error, output) {
// access output.sourceMap as above
});
Using the hash configuration specifying both optimization levels, e.g.
new CleanCSS({
level: {
1: {
all: true,
normalizeUrls: false
},
2: {
restructureRules: true
}
}
})
will apply level 1 optimizations, except url normalization, and default level 2 optimizations with rule restructuring.
All level 2 optimizations are dispatched here, and this is what they do:
recursivelyOptimizeBlocks - does all the following operations on a nested block, like @media or @keyframe;recursivelyOptimizeProperties - optimizes properties in rulesets and flat at-rules, like @font-face, by splitting them into components (e.g. margin into margin-(bottom|left|right|top)), optimizing, and restoring them back. You may want to use mergeIntoShorthands option to control whether you want to turn multiple components into shorthands;removeDuplicates - gets rid of duplicate rulesets with exactly the same set of properties, e.g. when including a Sass / Less partial twice for no good reason;mergeAdjacent - merges adjacent rulesets with the same selector or rules;reduceNonAdjacent - identifies which properties are overridden in same-selector non-adjacent rulesets, and removes them;mergeNonAdjacentBySelector - identifies same-selector non-adjacent rulesets which can be moved (!) to be merged, requires all intermediate rulesets to not redefine the moved properties, or if redefined to have the same value;mergeNonAdjacentByBody - same as the one above but for same-selector non-adjacent rulesets;restructure - tries to reorganize different-selector different-rules rulesets so they take less space, e.g. .one{padding:0}.two{margin:0}.one{margin-bottom:3px} into .two{margin:0}.one{padding:0;margin-bottom:3px};removeDuplicateFontAtRules - removes duplicated @font-face rules;removeDuplicateMediaQueries - removes duplicated @media nested blocks;mergeMediaQueries - merges non-adjacent @media at-rules by the same rules as mergeNonAdjacentBy* above;If clean-css encounters invalid CSS, it will try to remove the invalid part and continue optimizing the rest of the code. It will make you aware of the problem by generating an error or warning. Although clean-css can work with invalid CSS, it is always recommended that you fix warnings and errors in your CSS.
Example: Minify invalid CSS, resulting in two warnings:
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
a {
-notarealproperty-: 5px;
color:
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'div{margin:5px}',
stats: {
efficiency: 0.8695652173913043,
minifiedSize: 15,
originalSize: 115,
timeSpent: 1
},
errors: [],
inlinedStylesheets: [],
warnings: [
"Invalid property name '-notarealproperty-' at 4:8. Ignoring.",
"Empty property 'color' at 5:8. Ignoring."
]
}
Example: Minify invalid CSS, resulting in one error:
const CleanCSS = require("clean-css");
const output = new CleanCSS().minify(`
@import "idontexist.css";
a {
color: blue;
}
div {
margin: 5px
}
`);
console.log(output);
// Log:
{
styles: 'a{color:#00f}div{margin:5px}',
stats: {
efficiency: 0.7627118644067796,
minifiedSize: 28,
originalSize: 118,
timeSpent: 2
},
errors: [
'Ignoring local @import of "idontexist.css" as resource is missing.'
],
inlinedStylesheets: [],
warnings: []
}
An example of how you can include clean-css in gulp
const { src, dest, series } = require('gulp');
const CleanCSS = require('clean-css');
const concat = require('gulp-concat');
function css() {
const options = {
compatibility: '*', // (default) - Internet Explorer 10+ compatibility mode
inline: ['all'], // enables all inlining, same as ['local', 'remote']
level: 2 // Optimization levels. The level option can be either 0, 1 (default), or 2, e.g.
// Please note that level 1 optimization options are generally safe while level 2 optimizations should be safe for most users.
};
return src('app/**/*.css')
.pipe(concat('style.min.css'))
.on('data', function(file) {
const buferFile = new CleanCSS(options).minify(file.contents)
return file.contents = Buffer.from(buferFile.styles)
})
.pipe(dest('build'))
}
exports.css = series(css)
There is a number of 3rd party plugins to popular build tools:
See CONTRIBUTING.md.
First clone the sources:
git clone git@github.com:clean-css/clean-css.git
then install dependencies:
cd clean-css
npm install
then use any of the following commands to verify your copy:
npm run bench # for clean-css benchmarks (see [test/bench.js](https://github.com/clean-css/clean-css/blob/master/test/bench.js) for details)
npm run browserify # to create the browser-ready clean-css version
npm run check # to lint JS sources with [JSHint](https://github.com/jshint/jshint/)
npm test # to run all tests
Sorted alphabetically by GitHub handle:
@import processing;@import processing inside comments;minify method source traversal in ES6;sys package;@import inlining and URL rebasing.@import inlining behavior;clean-css is released under the MIT License.