A comparison of CSS minification engines.
Run the tests with:
node cli
Optionally, you can localise to an engine, or engines, of your choice by passing
the --engines
flag, with a comma separated list of engines:
node cli --engines cssnano,csso
And you can also run specific tests by passing the --suites
flag.
node cli --suites 19,84
This project has been heavily inspired from GoalSmashers' benchmark, covering Node.js minifiers on a variety of CSS frameworks. But rather than comparing minification sizes at a macro-level, it intends to provide a suite of test cases on what the optimiser actually compresses. The aim of this project is to improve the quality of Node.js minifiers (and provide thorough test cases for new projects) - so that it isn't necessary to run several minifiers on a single CSS file.
See https://github.com/ben-eb/css-minifiers for details on which engines are covered.
The test cases do not resemble a standard unit test style of pass/fail, because each minifier may yield slightly different output to the others, even when the ending CSS size is the same. So they are graded on these criteria instead:
The minification path chosen is considered 'above and beyond' what one would expect from a minifier; it either improved the structure of the CSS without losing its semantics, or it did some other transform which was beneficial. Note that this criteria does not appear on the majority of test cases.
The minifier performed a safe optimisation which reduced the size of the CSS.
The size of the minified code is greater than an optimal optimisation, but it is still fine to use in production.
The optimisation either breaks the author's original intentions, or a browser's capability to render the CSS generated by the minifier. Or worse, the minifier crashed when trying to parse the test case.
Some of these tests may appear deliberately contrived to try and break the minifiers. Indeed, there are some tests that contain CSS that no sane person would write. However, this is intentional; it is entirely possible that such code can end up in minified stylesheets, through aggressive optimisations such as selector and declaration merging. By testing every circumstance, we can ensure that any code we throw at the minifier will not break our stylesheets.
Note that if an optimisation is classified as sub-optimal, it is still fine to use in production code. But, we can always do better - so that if only one minifier supports a safe optimisation which yields a smaller result than the rest, it will be the recommended (optimal) solution.
This test suite should not be a replacement for thorough unit testing of a CSS minifier - it should be used alongside one.
When searching for different CSS optimisation tools as part of my build process, it was found that running more than one tool on a single CSS file resulted in greater compression than using a single tool. So why is this? Well, many CSS compressors that exist out there today perform optimisations other than simple whitespace compression. The more advanced ones will apply structural minimisation to CSS; code that optimises the structure of a CSS file beyond the author's original intent. A simple example:
h1 {
color: red;
}
h1 {
font-weight: bold;
}
Structurally optimised, this can look like:
h1{color:red;font-weight:bold}
Whereas a traditional minifier will just collapse the spacing around the ruleset:
h1{color:red}h1{font-weight:bold}
This kind of optimisation is important when considering that many developers use CSS frameworks today; it means that authors can write additional styles on top of the framework, and have the minifier compress those rules together automatically.
Thinking of contributing? Great! Fork away, do your work on a separate branch and send a pull request with your feature or fix. The aim of this project is to produce a robust suite of test cases that are small in scope; please do not contribute test cases that contain a full site's CSS. If you think that this project is missing test cases that you feel are relevant, then please:
- Fork the project and create a branch for your test case.
- Add a
camelCased
test directory intests
. - Create a
fixture.css
with your input. As much as possible, make your input test a limited case, so that tests can be small and meaningful. - Create an
optimalX.css
with the expected minified CSS - where X is any number of cases. - If some minifiers produce broken output, log it into
brokenX.css
- where X is any number of cases. - If some minifiers produce better than optimal output, log it into
outstandingX.css
- where X is any number of cases. - Optionally, add a README.md in the folder for further test case description.
- Commit your changes and send the pull request.
- You're awesome. :)
MIT © Ben Briggs