clean-css、cssnano、postcss-clean 和 uglifycss 都是用于减小 CSS 文件体积的 npm 包,但它们的底层机制、集成方式和优化策略截然不同。clean-css 是一个独立的、高性能的 CSS 优化器,专注于深度语法分析和安全压缩;cssnano 是基于 PostCSS 生态系统的插件集合,通过模块化插件链提供极致的体积优化;postcss-clean 是将 clean-css 的核心能力封装为 PostCSS 插件的桥梁方案;而 uglifycss 是一个较老的独立工具,采用简单的正则匹配进行压缩,功能相对基础。在现代前端工程化体系中,选择哪款工具取决于你的构建流程是否基于 PostCSS、对压缩率的极致追求程度以及对构建稳定性的要求。
在前端构建流程中,CSS 压缩是减少网络传输量、提升页面加载速度的关键步骤。虽然 clean-css、cssnano、postcss-clean 和 uglifycss 都能实现“压缩”这一目标,但它们的实现原理、集成方式以及对现代 CSS 特性的支持程度存在巨大差异。作为架构师,理解这些差异对于构建稳定、高效的工程体系至关重要。
工具的核心差异在于它们如何“理解”CSS 代码。这直接决定了压缩的安全性和优化深度。
clean-css 使用专门的词法分析和语法分析器将 CSS 转换为抽象语法树(AST)。这意味着它“理解”选择器、属性和值的结构,从而能安全地进行高级优化。
// clean-css: 基于 AST 的安全优化
const CleanCSS = require('clean-css');
const source = 'div { color: red; color: blue; }';
// 它能识别重复属性并安全移除前者
const output = new CleanCSS().minify(source).styles;
// 结果:div{color:blue}
cssnano 同样基于 AST,但它依赖于 PostCSS 解析器。它的优势在于模块化,通过一系列插件(如 cssnano-preset-default)对 AST 进行多次遍历和优化。
// cssnano: 基于 PostCSS AST 的插件链
const postcss = require('postcss');
const cssnano = require('cssnano');
const source = 'div { color: red; color: blue; }';
postcss([cssnano])
.process(source, { from: undefined })
.then(result => {
// 结果:div{color:blue}
console.log(result.css);
});
postcss-clean 本质上是在 PostCSS 管道中调用了 clean-css 的引擎。它先将 CSS 转为 PostCSS AST,再序列化回字符串传给 clean-css 处理,最后将结果写回。
// postcss-clean: 在 PostCSS 中调用 clean-css 引擎
const postcss = require('postcss');
const clean = require('postcss-clean');
const source = 'div { margin: 10px 10px 10px 10px; }';
postcss([clean()])
.process(source, { from: undefined })
.then(result => {
// 结果:div{margin:10px}
console.log(result.css);
});
uglifycss 采用简单的正则表达式和字符串替换。它不解析 CSS 结构,因此无法识别上下文,容易在处理复杂选择器或特定语法糖时出错。
// uglifycss: 基于正则的简单处理
const uglifycss = require('uglifycss');
const source = 'div { color: red; color: blue; }';
// 它可能无法智能处理重复属性,或者在某些边缘情况下破坏代码
const output = uglifycss.processString(source);
// 结果可能不够优化,甚至保留冗余代码
集成方式决定了工具在构建系统中的灵活性和配置复杂度。
clean-css 既可以作为 Node.js 库直接调用,也提供功能强大的 CLI 工具,方便集成到任何构建脚本或 CI/CD 流程中,无需依赖特定的构建器。
// clean-css: 独立的 CLI 使用
// 命令行直接运行,无需配置 webpack 或 gulp
// cleancss -o styles.min.css styles.css
// 或者在 Node.js 中独立使用
const { minify } = require('clean-css');
const output = await minify('styles.css', { sourceMap: true });
cssnano 设计为 PostCSS 插件,必须依附于 PostCSS 运行。这意味着如果你的项目已经使用了 Tailwind CSS、Autoprefixer 等 PostCSS 插件,集成 cssnano 几乎零成本。
// cssnano: 必须作为 PostCSS 插件配置
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
cssnano: { preset: 'default' } // 直接插入插件链
}
}
postcss-clean 也是 PostCSS 插件,但它的主要价值在于让习惯 clean-css 配置风格的团队能在 PostCSS 流程中继续使用熟悉的选项。
// postcss-clean: 在 PostCSS 配置中使用 clean-css 选项
// postcss.config.js
module.exports = {
plugins: {
'postcss-clean': {
level: 2, // 使用 clean-css 的 level 2 优化
rebase: false
}
}
}
uglifycss 主要提供 CLI 接口和简单的 Node.js API,缺乏现代构建工具的深度集成支持,配置选项也非常有限。
// uglifycss: 简单的 CLI 调用
// uglifycss styles.css > styles.min.css
// Node.js API 非常基础
const uglifycss = require('uglifycss');
const result = uglifycss.processFiles(['styles.css'], { expandVars: true });
这是区分专业工具与基础工具的关键。高级优化能显著减小文件体积,但需要强大的解析器支持。
clean-css 支持 Level 1(安全)和 Level 2(激进)优化。Level 2 可以重组选择器、合并相邻规则、移除覆盖的属性,甚至优化字体定义。
// clean-css: Level 2 深度优化
const source = `
.box { margin: 10px 10px 10px 10px; }
.box { padding: 5px; color: red; }
.box { color: blue; }
`;
// Level 2 会合并规则块并移除被覆盖的 color: red
const output = new CleanCSS({ level: 2 }).minify(source).styles;
// 结果:.box{margin:10px;padding:5px;color:blue}
cssnano 的默认预设(default preset)包含了一系列激进的优化插件,如 mergeRules(合并规则)、discardDuplicates(丢弃重复)、reduceInitial(将 initial 替换为具体值)等,通常能比 clean-css 获得更小的体积。
// cssnano: 自动执行多种优化策略
const source = `body { color: initial; } .a { color: red; } .a { color: red; }`;
// 自动将 initial 转换为具体值(如果配置了浏览器范围),并合并重复选择器
// 结果可能为:body{color:#000}.a{color:red} (取决于 browserslist)
postcss-clean 的优化能力完全等同于 clean-css,因为它只是透传配置。你能达到的优化深度取决于你传入的 level 参数。
// postcss-clean: 能力同 clean-css
// 配置 level: 2 即可开启相同的高级优化
// 效果与直接使用 clean-css 一致,只是运行在 PostCSS 管道中
uglifycss 仅能执行基本的空格移除、注释删除和简单的分号优化。它无法合并选择器,也无法识别并移除被后续规则覆盖的属性。
// uglifycss: 仅做表面清理
const source = `.a { color: red; } .a { color: blue; }`;
// 无法移除被覆盖的 color: red,也无法合并选择器
// 结果:.a{color:red}.a{color:blue}
在处理复杂的现代 CSS(如 CSS Variables, Nesting, Custom Properties)时,工具的稳健性尤为重要。
clean-css 对 CSS 标准遵循严格,对未知属性通常采取保守策略(保留但不优化),因此在处理新特性时较为安全。它明确支持 CSS Variables 的基本压缩。
// clean-css: 安全处理未知属性
const source = `:root { --main-color: #06c; } h1 { color: var(--main-color); }`;
// 能够正确保留变量定义和引用,不会破坏语法
cssnano 依赖于 browserslist 配置。如果配置不当,它可能会过度优化,移除某些旧浏览器需要但新浏览器不需要的前缀,或者反之。它对 CSS Variables 的支持很好,但需要确保插件版本最新。
// cssnano: 依赖 browserslist 配置
// package.json
"browserslist": ["> 1%", "last 2 versions"]
// 如果配置过旧,可能会错误地移除必要的现代语法结构
// 需确保 postcss 和 cssnano 版本与项目需求匹配
postcss-clean 继承了 clean-css 的安全性,但在 PostCSS 转换过程中,如果前面的插件(如某些实验性插件)生成了非标准 CSS,可能会在序列化阶段出现问题。
// postcss-clean: 注意管道上游的影响
// 如果上游插件输出了非标准语法,clean-css 引擎可能会报错或忽略
// 建议放在插件链的最后阶段运行
uglifycss 风险最高。由于基于正则,它极易破坏包含特殊字符、复杂选择器或 CSS Variables 的代码。官方已长期未更新,不建议处理现代 CSS。
// uglifycss: 高风险
const source = `.btn:hover { color: var(--color-primary); }`;
// 可能会错误地解析括号或连字符,导致生成的 CSS 无效
// 强烈避免在生产环境使用
| 特性 | clean-css | cssnano | postcss-clean | uglifycss |
|---|---|---|---|---|
| 解析引擎 | 专用 AST | PostCSS AST | 专用 AST (封装) | 正则表达式 |
| 优化深度 | ⭐⭐⭐⭐ (可配置) | ⭐⭐⭐⭐⭐ (极致) | ⭐⭐⭐⭐ (同 clean-css) | ⭐ (基础) |
| 集成方式 | 独立 CLI / Lib | PostCSS 插件 | PostCSS 插件 | 独立 CLI / Lib |
| 安全性 | 高 | 高 (需配置) | 高 | 低 |
| 维护状态 | 活跃 | 活跃 | 一般 | ⚠️ 停滞/不推荐 |
| 适用场景 | 通用/独立构建 | PostCSS 生态/极致优化 | PostCSS + 偏好 clean-css | 仅限旧项目维护 |
在现代前端架构中,cssnano 通常是首选,特别是当你的项目已经使用了 PostCSS 生态(如 Vue CLI, Create React App, Vite 等)。它能提供最小的输出体积,并且与 Autoprefixer 等工具无缝协作。
如果你的构建流程不依赖 PostCSS,或者你需要一个独立的、可预测性极强的压缩工具(例如在简单的 Node 脚本或非标准构建器中),clean-css 是最稳健的选择。它的 API 稳定,文档详尽,且 Level 2 优化足以应对绝大多数场景。
postcss-clean 是一个特定的“胶水”方案,仅在你必须使用 PostCSS 但又因历史原因或特定配置需求离不开 clean-css 内核时才考虑。
至于 uglifycss,除非你正在维护一个五年前的老项目且不敢改动构建配置,否则绝对不要在新项目中使用。它的技术原理已过时,存在破坏代码的风险,且社区已不再活跃。
最终结论:追求极致体积选 cssnano;追求独立稳定选 clean-css;远离 uglifycss。
如果你的构建流程不依赖 PostCSS,或者你需要一个独立、稳定且经过长期验证的 CSS 优化器,clean-css 是最佳选择。它提供了丰富的 API 配置项,允许你在压缩率和安全性之间进行精细调整,特别适合需要独立 CLI 工具或非 Node.js 环境集成的场景。
如果你已经在使用 PostCSS 构建流程(如通过 Autoprefixer、Tailwind CSS 等),cssnano 是自然且最强大的选择。它利用 PostCSS 的 AST 解析能力,能够执行比传统工具更深层的优化(如合并选择器、移除冗余属性),适合追求极致包体积优化的现代项目。
当你需要在 PostCSS 管道中利用 clean-css 特有的算法或配置选项,但又不想完全迁移到 cssnano 的插件体系时,可以选择 postcss-clean。它是一个轻量级的适配器,适合那些对 clean-css 有特定依赖但又必须融入 PostCSS 工作流的遗留项目或特殊场景。
仅建议在维护极老旧的项目或对 CSS 压缩要求极低(仅需去除空格和注释)的简单脚本中使用。由于其基于正则的解析方式无法理解复杂的 CSS 结构,存在破坏合法 CSS 代码的风险,因此不建议在任何新的生产级项目中使用,应优先评估 clean-css 或 cssnano 作为替代方案。
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.