barryvan

CSS minifier and alphabetiser

Update: This project is now hosted on GitHub: https://github.com/barryvan/CSSMin/

There are quite a few CSS minifiers out there, which can bring the raw size of your CSS files down substantially. There are, however, significant gains to be made if the CSS is minified so that it gzips better. To that end, I’ve written a small Java application that will read in a CSS file and output its contents to stdout or another file in a format that’s optimised for gzipping.

The problem

A gzipped file will be stored most efficiently when there are many recurring strings in the file. This means that when writing CSS files, this code:

.pony {
border: solid red 1px;
font-weight: bold;
}
.lemur {
border: solid red 1px;
font-weight: normal;
}

will be better-compressed than this:

.pony {
border: solid red 1px;
font-weight: bold;
}
.lemur {
font-weight: normal;
border: red solid 1px;
}

In the first sample, notice that we have a very long string that occurs twice:

 {
border-style: solid red 1px;
font-weight: 

In the second sample, there are strings that occur more than once, but they’re much shorter. The gzip algorithm can, in the first case, replace that entire long string with a much shorter placeholder.

What it does

So, how can we optimise CSS for gzipping, then? A file that’s minified using this CSS Minifier will have these operations applied:

  • All comments removed.
  • The properties within all selectors ordered alphabetically.
  • The values for all properties ordered alphabetically.
  • All unnecessary whitespace removed.
  • Font weights replaced by their numeric counterparts (which are shorter).
  • Quotes stripped wherever possible.
  • As much text as possible transformed to lowercase.
  • Prefixed properties (for example, -moz-box-sizing) placed before the unprefixed variant (box-sizing).
  • Colours simplified from rgb() to six- or three-digit hex values, or simple names.
  • Units on values of 0 stripped.
  • Multi-parameter items simplified to as few parameters as possible.
  • Various other small tweaks and adjustments made.

By way of example, the following CSS snippet:

body {
  padding: 8px;
  margin: 0;
  background-color: blue;
  color: white;
  font-family: "Trebuchet MS", sans-serif;
}

h1 {
  margin: 0;
  padding: 0;
  font-size: 200%;
  color: #0F0;
  font-weight: bold;
}

p {
  margin: 0 0 2em;
  line-height: 2em;
}

would be formatted to the following (note that line breaks have been added for legibility — no line breaks appear in the final output):

body{background-color:blue;color:#fff;font-family:"trebuchet ms",sans-serif;
margin:0;padding:8px}h1{color:#0f0;font-size:200%;font-weight:700;margin:0;
padding:0}p{line-height:2em;margin:0 0 2em}

Compression results

These are the results of compressing the main CSS file for one of the webapps I develop at work.

  Original size (bytes) Gzipped size (bytes)
Plain 81938 12291
YUI 64434 10198
LotteryPost 63609 10165
CSS Drive 69275 10795
CSSMin 63791 9896

Download

Head over to GitHub to download the source.

Usage

First, if you haven’t done so yet, compile the code:

# javac CSSMin.java

Then, you can call the minifier by running

# java CSSMin in.css [out.css]

If you do not specify an output file, the resultant CSS will be printed to stdout (and can then be redirected as you wish).

Contact

If you have any questions or comments about this app, or if you find a bug or some weird behaviour, just comment on this post, and I’ll see what I can do.

You can also raise issues on GitHub, fork the project, commit changes, and more.

If you find this utility useful, let me know!

Oliver Nassar

Very cool. Never seen this done :)

Barry van Oudtshoorn

Thanks Oliver! I’ve updated the post with a chart comparing my minifier against YUI’s, both in terms of raw output size and final gzipped size.

Barry van Oudtshoorn

Yes, you’re right, Ray, that could result in much more significant compression performance. The only difficulty with implementing it for CSS files is that I would have to then have some knowledge of the document that the CSS is to be applied to, to ensure that rules cascade correctly.

Whereas in Javascript functions can largely be reordered without any problems, because the script does not depend on the document order, CSS uses the order that items appear in the document to determine whether they are overridden.

Still, I will definitely investigate this further — I am sure that there are some optimisations that can be done in this direction quite safely.

Andrew Sutherland

Nice work. It seems to me this would be better as a patch to YUI — the YUI compressor has already taken care of a lot of the problems and micro-optimizations and edge cases of CSS through minor bugfixes, so it doesn’t make sense to re-invent the wheel. YUI Compressor is open-source and already adopted by a lot of people — what do you think?

Barry van Oudtshoorn

@Andrew: I understand what you’re saying. The difference, though, is that the YUI CSS compressor is essentially a string-replacement system, whereas my optimiser actually parses the CSS file. It’d essentially be a complete rewrite. :) That being said, I have looked through the code of the YUI compressor, and I’ve certainly not been shy in realising that I’d missed an edge case or whatever.

There’s still a lot of work to be done on this, but hopefully it’ll become a nice robust CSS minification package over time.

Robert Schultz

Nice work and I enjoyed the post. I discovered this blog through Ajaxian and have just subscribed to it’s RSS feed :)

One thought of potential concern, I know that some CSS orders properties within a selector in a certain order so that IE will do the correct behaviors. I believe in some cases property order DOES matter.
I haven’t researched this further, but I seem to recall this being the case.

I personally will probably continue to use the YUI CSS compressor as it’s already part of my build process, right alongside using it’s JS compressor.

I look forward to reading future blog posts from you :)

Fabian

Thats interesting. I am pretty sure this helps, but how about something like this:

html{
height:100%;
max-height:100%;
/* hide overflow:hidden from IE5/Mac */
/* \*/
overflow: hidden;
/* */
}

or any other css hacks?

Aike

i am not sure, but isn’t grouping by selector better?
like:

.pony {
border: solid red 1px;
font-weight: bold;
}
.lemur {
border: solid red 1px;
font-weight: normal;
}
——
to:

.pony, .lemur {
border: solid red 1px;
font-weight: normal;
}

Steve Clay

Could you publish your test CSS file?

@Fabian: Minify’s CSS compressor preserves many comment hacks.
Can you publish your test CSS file so we can compare other algorithms? :)

Steve Clay

@Fabian, disregard the last sentence. I meant to remove it.

Thomas J Bradley

Is it the fact that the properties are in alphabetical order helping compression?

Or just that the properties are in the same order throughout all the selectors?

Peter Michaux

Nice idea about reordering properties to improve gzipping. I don’t think a fixed ordering (e.g. alphabetical) will be optimal, however. Suppose I have the following:

body {
color: blue;
border: 1px solid red;
padding: 10px;
}

div {
border: 1px solid red;
padding: 10px;
}

Changing the order of border and color in the body will decrease the repetition and so increase the gzipped size.

An optimization technique (something in the realm of genetic algorithms) could be used to determine an optimal ordering which would be unique to the particular source file.

Barry van Oudtshoorn

@Robert: Thanks for the subscription! Glad you’re enjoying the blog. :)

At the moment, everything is just alphabetised, so if you rely on property order for something, the process may well break it.

Barry van Oudtshoorn

@Fabian: At the moment, the system will strip out any and all comments, without regard for their use in hacks, so your example will result in the “overflow: hidden” not being hidden. :/

Barry van Oudtshoorn

Yes, grouping by selector would be much better — it’s actually an optimisation that I’ve considered in the past. In the example, though, your final output would have to be something like

.pony, .lemur {
border: solid red 1px;
font-weight: normal;
}
.pony {
font-weight: bold;
}

Barry van Oudtshoorn

Unfortunately I can’t publish the exact CSS file because of an NDA, but hopefully later today I will publish a ‘sanitised’ version of it. (Basically, ensuring that there aren’t any comments in there that shouldn’t be in there.) :)

Barry van Oudtshoorn

It’s the consistent ordering of the properties that results in better compression, rather than their alphabetical ordering. It just happens that ordering alphabetically is a) easier to implement, and b) semantically simpler (for me at least).

Barry van Oudtshoorn

You’re right, Peter, there are further ordering optimisations that can be made. I will definitely investigate implementing a more ‘intelligent’ ordering of the properties based on their frequency within the file.

Peter Michaux

Barry, Ordering based on the frequency in the file may be better but it won’t lead to an optimal ordering either. An optimization algorithm (e.g. something like simulated annealing or genetic algorithms) with the gzip size as the cost function will (probabilistically.)

John Ryding

@Barry in one of your comments you noted “my code actually parses the string replacement while YUI has a string replacement system.”

Could you elaborate more on this? Did you actually build a java based CSS parser, or are you using regular expressions to do the replacements and minifying?

Thanks.

Barry van Oudtshoorn

@John: In a way, yes. Basically, I have a bunch of classes that represent the various ‘bits’ that make up a CSS file: Selectors, Properties, and what I’m calling “Parts” (comma separated values for properties). So the way my system works is that it basically parses the CSS file into a bunch of these classes — selectors have properties, and properties have parts. This means that, unlike YUI, I’m not looking at the ENTIRE file when I’m minifying — rather, I’m dealing with it on a much smaller scale. Regular expressions are still used within the parts themselves, and I also do some basic pre-formatting before I actually parse the file, like removing comments and tabs, newlines and carriage returns.

Kay.L

zero value give error
p{ padding:0}

sunnybear

Web Optimizer ( http://code.google.com/p/web-optimizator/ ) incorporates several ways of CSS/JS minimization and use CSS rule hash to complete CSS Sprites / data:URI transformation. Can CSS Minifier return any kind of array of computed CSS rules? It will great to apply it to Web Optimizer logic.

Web Optimizer automates all possible clientside optimization actions for PHP websites.