Currently Browsing: Home » Stunningly Simple CSS Minifier

Stunningly Simple CSS Minifier

Creating a beautiful and eye-catching website can often require you to have a hefty CSS file. Unfortunately, this side-effect takes away from your visitors experience; they spend more of their time downloading rather than viewing. In essence, a balance needs to be reached in order for full optimization.

CSS

Solving the problem

One simple way to achieve this balance is by using a minifier. By taking a CSS file in as input, a minifier removes white space and other unnecessary elements to significantly reduce file size.

Oops, did you forget about something?

At first glance, this seems to be a zero-drawback system. Upon closer inspection, it becomes apparent that removing white space creates poor formatting, a leading cause of what we call read-once write-once code. You might be able to edit the styles directly after creation, but in a few days or weeks, this task becomes tedious.

This is mainly why using online minifiers aren’t 100% supported. In fact, by creating your own minifer, there is a way to both keep formatting and produce minified code. You might think these are mutually exclusive. This post, which demonstrates how to create a stunningly simple CSS minifier, aims to prove you wrong.

Meet our dear friend: PHP

In our previous post, we showed you how to generate CSS using PHP. In this article, we will utilize a similar concept.

Before you can actually solve the minifier’s main problem, it is imperative to first write a minifier. Luckily, we have created an extremely simple PHP function to do so. In approximately 10 lines, the following code will finish over 90% of all possible minifications:

function minify( $css ) {
	$css = preg_replace( '#\s+#', ' ', $css );
	$css = preg_replace( '#/\*.*?\*/#s', '', $css );
	$css = str_replace( '; ', ';', $css );
	$css = str_replace( ': ', ':', $css );
	$css = str_replace( ' {', '{', $css );
	$css = str_replace( '{ ', '{', $css );
	$css = str_replace( ', ', ',', $css );
	$css = str_replace( '} ', '}', $css );
	$css = str_replace( ';}', '}', $css );

	return trim( $css );
}

Who knew simplifying CSS could be that easy? Using preg_match, the above code is able to replace extraneous white space with just a single character. It then proceeds to minfy even more elements using str_replace.

Huh? How am I supposed to use the above code?

Well, now that we have created our PHP function, you might wonder how to use it. The answer to this question stems, once again, from the previous article. Instead of linking to a css file from our HTML, we will actually refer to a php one:

<link rel="stylesheet" type="text/css" href="minify.php?css=styles.css" />

Notice how we call our minify.php file with the parameter css. The value of css actually refers to the file we want to minify. With this in place, create the minify.php file and add in the function we previously created. In addition, place the following code at the beginning of the file:

header( "Content-type: text/css" );

$file = isset( $_GET[ 'css' ] ) ? $_GET[ 'css' ] : '';
if( !file || array_pop( split( '\.', $file ) ) != 'css' || strpos( $file, '/' ) !== false )
	die( 'Invalid Parameters' );

$content = @file_get_contents( $file );

The above snippet obtains the contents of the specified css file. To add security, it first verifies that the file name is not blank, ends in .css, and that it is located in the current directory. Feel free to edit the last setting as necessary by removing:

strpos( $file, '/' ) !== false

To complete our CSS minifier, all we have to do is call minify on the obtained content and then print it:

echo minify( $content );

Voila! You now have a fully functioning CSS minifier in minimal code. In addition, the formatting of your code stays 100% in-tact. All you need to do is edit the CSS file that you pass to minify.php in order to make changes.

Here is a simple copy-paste version of our code:

<?php
header( "Content-type: text/css" );

$file = isset( $_GET[ 'css' ] ) ? $_GET[ 'css' ] : '';
if( !file || array_pop( split( '\.', $file ) ) != 'css' || strpos( $file, '/' ) !== false )
	die( 'Invalid Parameters' );

$content = @file_get_contents( $file );
echo minify( $content );

function minify( $css ) {
	$css = preg_replace( '#\s+#', ' ', $css );
	$css = preg_replace( '#/\*.*?\*/#s', '', $css );
	$css = str_replace( '; ', ';', $css );
	$css = str_replace( ': ', ':', $css );
	$css = str_replace( ' {', '{', $css );
	$css = str_replace( '{ ', '{', $css );
	$css = str_replace( ', ', ',', $css );
	$css = str_replace( '} ', '}', $css );
	$css = str_replace( ';}', '}', $css );

	return trim( $css );
}
?>

Thanks for stopping by!

This entry was posted on Sunday, September 13th, 2009 at 02:28:56. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

27 Responses to “Stunningly Simple CSS Minifier”

  1. Taimur says:

    Simply Great post .. Thanks so much

  2. SeanJA says:

    Alternatively you could use csstidy prior to uploading your css… then you don’t have to parse it every single time. CSS tidy can also combine parts of css to make it smaller and more efficient. http://csstidy.sourceforge.net/

    • @Taimur: Thank you!

      @SeanJA: I do agree that CSS tidy is a nice minifier, but the fact that you must apply it before every upload creates extra work. This problem is mentioned and addressed in the article.

      @Jason: I agree with you 100%. All of the additions you listed are extremely worthwhile and useful. As for the article, I wanted to present the basics and expand on them in the future. Thank you for your input. I should have a more advanced post up soon!

      @Toby: Thank you for your sharp eye! I forgot to add comment stripping. The article should be updated soon.

      On a side-note, the post actually does contain a section mentioning the directory rule.

  3. Jason Huck says:

    Not a bad start, but you could go much further with it.

    For example, one simple improvement would be to modify your minification script to accept multiple CSS files and concatenate them into a single file before minifying them. This would provide the additional benefit of reducing the total number of HTTP requests, which can provide a significant performance boost.

    If you have access to run command line processes, you can apply the same concept to your javascript files as well, and run them through a thorough, well-tested minifier like YUI Compressor.

    You could also cache the resulting output after the first pass, and then check the modification dates on the source files on subsequent requests. If nothing has changed, you don’t need to re-process all of your source files – just serve up the cached version.

    There are plenty of other things you can do; don’t forget about configuring your web server to gzip compress your scripts and styles, for instance. I cover some of the basics in the paper and slides available here:

    http://devblog.jasonhuck.com/2008/09/23/an-asset-management-system-for-lasso-powered-sites/

    - jason

  4. Toby says:

    This is an interesting approach, but the real-world gains seem small. I just fed a 600+ line css file to it, and according to Firebug, the minified version weighs in at 6K vs. the 7K original. It’s worth mentioning that unless you keep the minify.php script in the same dir as your css (say, example.com/css/ ) you’ll need to tweak the path to point to your CSS files. I concatenated mine in the file_get_contents, as @file_get_contents( ‘css/’.$file ); myself.

    I’d think a bigger savings could be achieved by regexing out any comments in the CSS (though the cost in CPU would be higher). All of which makes mod_gzip, or using output buffering with compression, seem easier (assuming your host will support one or the other).

  5. Karl Agius says:

    Good start, but I really must agree with LC and Jason above – the gains from minification don’t offset the fact that the minification has to be performed every single time.

    Have you considered having php run yui compressor or similar on the server, save the output, and serve that instead of minifying it on the fly? You’d get the benefit of compression without taking the hit each time, and without having to do it manually yourself.

    That way you should also be able to process javascript files in the same way.

    • @Karl: I agree; it does take some extra time to minify. With most CSS files, this offset is quite negligible, but it can become a problem. I will definitely add caching in my next post to prevent excess minification. In addition, I will look into the YUI compressor. Thanks for your input!

  6. tay says:

    thanks for this :)

    however i had some problems with minified css not being readable in some cases – switching the positions of the first two (preg_)replaces solved it for me.

    taking it further..i actually combine all css (and js) files and save them as a single minified and compressed (gzip) file (also a file that’s not compressed for stone age browsers :) when the page is opened the first time. The next page request then gets those files with an expiration header and headers for content-type and content-encoding via php..actually I also do the same with pre-rendered html pages (using ob_start & co.) that have no need for db access every time they are visited.

    if bandwith is of concern..take a look at gzip..even with minified files it still makes something like 2kb from 8..

    greez

  7. Hi !

    Thanks for this article, I really enjoyed reading it. But this script didn’t work for me (I’m using Ubuntu and XAMPP with PHP 5.2.9), I had problems with the preg_replace, and with the security in the beginning. I also don’t understand the instruction “!file” (line 5), what does it mean ?

    Thanks to you, I made my own minifier, inspired by yours and others I found on the web. I added the caching and the concatenation of several files.

    (PS : I’m french, so please forgive my mistakes… ^^’ )

  8. @Adrian: Sorry for the late reply! I’m really glad this minifier helped.

    Addressing your question, !$file just checks whether file is an empty string (“”).

    Thank you for the encouraging remarks!

  9. sunnybear says:

    CSS minify (both simple straighforward — a bit better than described one — and CSS Tidy machine) is integrated into Web Optimizer — http://www.web-optimizer.us/ — which combines and minifies all CSS/JS code ‘on fly’ w/o additional server side load (minified files are gzipped and cached).

  10. Alex says:

    Hi,

    thanks for the snippet.
    I am using the function in a script of mine that concatenates all required css files.
    Problem I found was that the resulting minified file was larger than the original file.
    I added at the end of the function:

    $css = str_replace( array( “\r”, “\n”, “\t” ), ”, $css );
    return trim( $css );

    This removed carriage returns and line feeds.
    worked nicely, got a 42k concatenated file down to 32k.

    Thanks

  11. Eugene says:

    Since the whole point of minification is to increase page load speed, it is entirely silly to do the minification per resource request thus costing time.
    The idea is to have your scripts in a readable/editable dev environment, which exports to your prod env via your minification and whatever else scripts (like expiry and cache related headers or fingerprints).
    And that being the case, youre sometimes better off to simply embed the css or js into the html/xhtml file so no resource files are called in (other than images and objects), then just minify the whole thing.
    Unless there is a large sum of css and/or js in which case you should further concatenate and gzip the css and/or js separately, and just minify the html/xhtml.

    Rgds,
    Eugene.

  12. Eugene says:

    … I should add, that if you really must minify on the fly then you can add a module to your server. An example (if your running apache n mod_perl) is Apache2::Filter::Minifier::CSS which supports CSS:Minifier::XS which is pretty fast.
    But to me, this php (dont get me wrong I love php) inline approach is not an option.

    e

  13. kerido says:

    Hi. Your minifier is sure simple.
    However, you use regular expressions. On my blog post I came up with a different approach. It’s faster and produces very compact output. Here’s the address: http://www.ko-sw.com/Blog/post/An-Ultra-Fast-CSS-Minify-Algorithm.aspx. The code is in C#, however, it can be easily converted to PHP. There is no fancy stuff there.
    Anyway, thanks for the post!

  14. Terri Ann says:

    Thanks this was incredibly helpful to minify on the fly!

  15. Great post! You can minify CSS even more if you change the regexes to handle newlines as well as spaces.

    Example:

    $css = str_replace( ‘;\w+’, ‘;’, $css );

  16. Pete says:

    Awesome post. I had tried using minify on my wordpress but everytime the CSS was minified using wp-minify plugin, the layout broke. Will try this now.

  17. Mark says:

    Thanks man that is simple. And it works. Other scripts I’ve used blow out my webkit styles and only compress about 16%. Yours got me 17.7% savings!

  18. Guido Parakaz says:

    Uhhhm, if you use ASP.NET (yeah, Microsoft, I know), it gets a lot simpler with this:
    http://www.codeproject.com/KB/aspnet/CombineAndMinify.aspx

    It does JavaScript as well as CSS plus lots more performance enhancing stuff.

  19. Very nice. I have a cleaning script I run when publishing my projects live. It reads all css files, merges them into 1, compresses them, then saves the production file.

    For my most recent project CSSTidy was breaking the css and only saving 11%.

    Using this simple function I was able to shave off 25.1% on the merged css files :)

    Thanks for the post!

  1. CSS Minification on the Fly | Shiny Blog
  2. On Demand CSS Minification Trick | Naked Trout
  3. SEO Blog | Tips To Improve Your Page Load Speed
  4. Part II: Managing CSS and JavaScript files within a Zend Framework App | Leaking Abstraction
  5. La guerre aux millisecondes

Leave a Reply

Want to be notified when someone replies? Subscribe to this post's comment RSS feed.
Any field marked with a * is required.