Improve ASP.NET Performance - CSSmin

If you follow Douglas Crockford's work, you might know about JSMin, a bit of code written for various languages to optimize JavaScript to make it smaller. One thing it does is makes everything go to one line, eliminating some of the space by removing white space.

In Web 2.0 applications, CSS is all you have for styles. In most apps, the CSS can get large really quick. One way to improve your performance (of not only ASP.NET apps) is to minify your CSS. Various people have created this sort of functionality for you, but you aren't given the flexibility of doing the minification yourself.

In my company's new website, part of the design decision was to always have an eye out for performance. To do this, we needed 1 CSS file and have it as small as possible. To do the "small as possible" part, we implemented a series of REGEX replacements that did all the work for minifying our CSS. Here's what we came up with:

 

C#:

 

   1: public static string CompressCSS(string body)
   2: {
   3:     body = Regex.Replace(body, "/\\*.+?\\*/", "", RegexOptions.Singleline);
   4:     body = body.Replace("  ", string.Empty);
   5:     body = body.Replace(Environment.NewLine + Environment.NewLine + Environment.NewLine, string.Empty);
   6:     body = body.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
   7:     body = body.Replace(Environment.NewLine, string.Empty);
   8:     body = body.Replace("\\t", string.Empty);
   9:     body = body.Replace(" {", "{");
  10:     body = body.Replace(" :", ":");
  11:     body = body.Replace(": ", ":");
  12:     body = body.Replace(", ", ",");
  13:     body = body.Replace("; ", ";");
  14:     body = body.Replace(";}", "}");
  15:     body = Regex.Replace(body, "/\\*[^\\*]*\\*+([^/\\*]*\\*+)*/", "$1");
  16:     body = Regex.Replace(body, "(?<=[>])\\s{2,}(?=[<])|(?<=[>])\\s{2,}(?=&nbsp;)|(?<=&ndsp;)\\s{2,}(?=[<])", string.Empty);
  17:  
  18:     return body;
  19: }

 

 

VB:

 

   1: Public Shared Function CompressCSS(ByVal body As String) As String
   2:   body = Regex.Replace(body, "/\*.+?\*/", "", RegexOptions.Singleline)
   3:   body = body.Replace("  ", String.Empty)
   4:   body = body.Replace(Environment.NewLine + Environment.NewLine + Environment.NewLine, String.Empty)
   5:   body = body.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine)
   6:   body = body.Replace(Environment.NewLine, String.Empty)
   7:   body = body.Replace("\t", String.Empty)
   8:   body = body.Replace(" {", "{")
   9:   body = body.Replace(" :", ":")
  10:   body = body.Replace(": ", ":")
  11:   body = body.Replace(", ", ",")
  12:   body = body.Replace("; ", ";")
  13:   body = body.Replace(";}", "}")
  14:   body = Regex.Replace(body, "/\*[^\*]*\*+([^/\*]*\*+)*/", "$1")
  15:   body = Regex.Replace(body, "(?<=[>])\s{2,}(?=[<])|(?<=[>])\s{2,}(?=&nbsp;)|(?<=&ndsp;)\s{2,}(?=[<])", String.Empty)
  16:  
  17:   Return body
  18: End Function

 

So you see it's just a few logical replacements. These few replacements makes a world of difference. The amount saved in bandwidth and caching is amazing.



kick it on DotNetKicks.com

UPDATE: For those who asked, this is how I use this code. First what I do is enumerate through The files in my CSS directory. Here is my static method which I have in a static "Logic" class (more on that later):

   1: public static IList<System.IO.FileInfo> GetFiles(string serverPath, string extention)
   2: {
   3:     if (!serverPath.StartsWith("~/"))
   4:     {
   5:         if (serverPath.StartsWith("/"))
   6:             serverPath = "~" + serverPath;
   7:         else
   8:             serverPath = "~/" + serverPath;
   9:     }
  10:  
  11:     string path = HttpContext.Current.Server.MapPath(serverPath);
  12:  
  13:     if (!path.EndsWith("/"))
  14:         path = path + "/";
  15:  
  16:     if (!Directory.Exists(path))
  17:         throw new System.IO.DirectoryNotFoundException();
  18:  
  19:     IList<FileInfo> files = new List<FileInfo>();
  20:  
  21:     string[] fileNames = Directory.GetFiles(path, "*." + extention, System.IO.SearchOption.AllDirectories);
  22:     foreach (string name in fileNames)
  23:         files.Add(new FileInfo(name));
  24:  
  25:     return files;
  26: }

 

Then what I do is get the CSS from the server and do the CSSmin:

   1: public static string CombineCSS()
   2: {
   3:     string allCSS = string.Empty;
   4:  
   5:     foreach (FileInfo fi in Logic.Files.GetFiles("~/Content/CSS/", "css"))
   6:     {
   7:         using (StreamReader sr = new StreamReader(fi.FullName))
   8:             allCSS += sr.ReadToEnd();
   9:     }
  10:  
  11:     allCSS = allCSS.Replace("~/", Global.BaseURL);
  12:  
  13:     allCSS = Compress(allCSS);
  14:  
  15:     return allCSS;
  16: }

 

And finally I put it into an MVC controller. What you could do is put the following in a generic handler if you don't use the MVC bits:

 

   1: [ControllerAction]
   2: public void AllCSS(string id)
   3: {
   4:     Response.ContentType = "text/css";
   5:  
   6:     Response.Write(Logic.CSS.CombineCSS());
   7: }

 

Hope that helps!

12 Comments

  • Nice.
    I found it saved a little under 25% over the wire (I used FireBug to view the file sizes). You'd definitely want to cache the output though.

  • I would love to see the ASP.NET team make Release-only build tasks to minify CSS and JS on deployment (and release builds). If it's already there (in the new Web Deployment Projects, for example), my apologies, Microsoft.

  • On reading this article I was a bit sceptic about using this method for decreasing the filesize of a css. However after trying this out on only a 4kb (3714 bytes) css file it decreased to 2899 bytes. Actually a 22% save at a small css file.

  • What about iis compression ?

  • @andrew myhre
    I agree. I do that as well in my MVC controller (which I will talk about in a few posts).

    @Rody van Sambeek
    Glad it worked for you! Any little bit that it saves will save your users.

    @Shannon
    Can you explain what you mean? I think I do the same thing... but I'm not quite sure what you are talking about. But I would be glad to hear what you are doing! :)

    @lkempe
    I'm not sure what the benift over using IIS Compression over an Application-level CSS manipulator. Mind explaining?

  • What's the point of this:

    body = body.Replace(Environment.NewLine + Environment.NewLine + Environment.NewLine, string.Empty);
    body = body.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
    body = body.Replace(Environment.NewLine, string.Empty);

    1. Remove any instance of three consecutive new-lines;
    2. Replace two consecutive new-lines with one;
    3. Remove all new-lines;

    You could achieve exactly the same effect without the first two lines:

    body = body.Replace(Environment.NewLine, string.Empty);

  • Can you tell me how to use these code, put in page load or what?
    i want a real example to call it if you please.

  • @amrelgarhy
    Take a look at the update.

  • placing these files into the pages itself is a nice way to do things if the files are specific only to this 1 page.

    If they are more general it would make more sence to generate a minified version and let that be cached from IIS + Gzipped.

    And offcourse you want to cache the .css output and not regenerate it on each hit.

    That said, nice thought on compressing the css.

  • You see, putting all the CSS into one makes better client side performance though. When you have multiple CSS, even though you do a client side cache, it causes more things to go accross the wire. This is something you don't want. Even though the one file is larger, you always want as little HTTP requests as possible so your pages load faster.

    This is what I've heard from a number of source, including Yahoo!. If you have some reasons to the contrary... let me know.

  • Does anyone knows how to do the same but for Javascript files?

    Thank You,
    Miguel

  • @shapper

    Use a JSMin utility. I recommend this version:

    http://svn.offwhite.net/svn/SmallSharpTools.Packer/trunk/ClassLibrary/JavaScriptSupport/JavaScriptMinifier.cs

    All you have to do is call JavaScriptMinifier.Minify(yourstring)

Comments have been disabled for this content.