Thursday, December 31, 2009

Ultimate automatic stylesheet combining, minification and .less integration

3/17/2010
Update to article here: http://blog.waynebrantley.com/2010/03/style-sheet-compression-and-less-add.html 


Combine your stylesheet css files into one file to improve site performance.
We all know this rule, but how do we go about doing this?   We could automate this in our build script, but then that means we have to do the same thing in development.   So,  if you reference 'combined.css' in your webpage in development - because the build server will make that for you - you have to maintain that combined file in development.  Or, you are looking at the debug setting and properly outputting a reference to the correct script(s).  Either way, this is a pain and makes something that works in development potentially broken at deployment.  What if the deployment script combined them in a different order than you had done in development?  This would be a difficult problem to find.  If we can avoid the development environment being different than the deployment environment, we should.


Minify your stylesheet css files into one file to improve site performance.
We also know this rule, but how do we go about doing this?   This has many of the same considerations as the combine problem above.  Difference is usually, your build process would just minify a stylesheet replacing the regular one with the new one.  This makes for MUCH less difference between development and deployment, so this one is not quite as painful.    However, what if the minification process breaks your stylesheet?  When and how will you find this?  Again, deployment different than development will open its jaws and bite you here.



Writing stylesheets violates the DRY principle, so try Less Css for .net.
This has it roots in ruby.  It lets you have variables, reference styles from other styles and other painful things you have to do in css.  As a quick example - here is a screenshot directly from their website:

As you can see, this is very useful.  It uses existing CSS syntax, so it will just work on all your existing stylesheets!  As an additional feature, when running through the less processor it can minify the stylesheet.  To implement this, you can run into the exact same problems as the 'minification' above.



Use an http handler to combine, minify, less, etc...
This is exactly how the Less Css for .net project implements the less processor.  However, sometimes people, like Phil Haack, just want a static css file.  So, Phil implemented a T4 template to take files with the name of .less and create a .css file.  Now, this is getting close to a good solution!   The build does not have to do anything but copy the stylesheet and our build vs development environment is closer alike.


Use Phil Haacks T4 template to minify
This definitively works.  However, you  have to remember to run the T4 template after each modification of a stylesheet, which means it is easy to break both the development and deployment environments!  So, I took his template and modified it to support the 'always keep template dirty' workaround.  This requires you to open the T4 template with your solution and then save the template once.  After that on each build/save it will regenerate the stylesheet.   This is an improvement, but not that much of one.  Plus this does not handle any style sheet combining.


Finally, the ultimate solution is built
I decided to write an add-in.   Never having written an add-in, I got a great head start from Martin From who sent me an add-in that captured some ide events and it showed me basic usage.  From there, I started enhancing.  I ended up with an add in that combines any stylesheets together, minifies and runs them through the less engine.  It can just combine or just minify or both and in any combination.   It is also totally automatic and requires no additional steps.   Additionally, if you have a combination of several stylesheets and one of those stylesheets change, it will automatically rebuild the combined one.   This solves all build/development environment concerns and gives us a great end result. 


How to use and configure it

Basic less processing:  First and easiest thing you can do is to create or rename your stylesheets as .less.css.   So, if you have a mysite.css, just rename it to mysite.less.css and instantly it will produce mysite.css that has been run through the less processor and is minified:



Style sheet combining:  To accomplish this, you create an XML file that defines how and what to combine.  If you want the combined stylesheet to be called final.css, then create an XML file called final.less.config.  Here is a screenshot and an example configuration.



<?xml version="1.0" encoding="utf-8"?>
<root>
    <CssFile Name="LessBased.less.css"/>
    <CssFile Name="MainSite.css"/>
    <CssFile Name="KeepAsIs.css" FilterThroughLess="False"/>
</root>

Let's just say for some reason you needed the "LessBased.css" file to be included by itself somewhere AND you needed that same file included in some of your combined css files.  The configuration above will do that.  It takes LessBased.less.css and MainSite.css, runs them through less processor, minifies them and combines that with an unmodified version of KeepAsIs.css.   Additionally, you could have included LessBased.css and marked it to NOT be filtered through less and minification - because that file already is.

In general, I think you would just put your 'less' syntax into your regular .css files, not renaming or doing anything.  Then using a final.less.config file, let them all be combined together.

Installation
Simply download the zip file and copy the contents into your Visual Studio Addin's folder (normally My Documents\Visual Studio 2008\AddIns).  Start visual studio and enjoy.  Currently I only have this for VS2008, however should be easy to make work on 2010.

What do you think?

1-2-2010 Update - dependency with css files in folders bug.

kick it on DotNetKicks.com
Shout it

15 comments:

Erik van Brakel said...

"This has it roots in java (like almost everything we do)."

Actually, lesscss is a ruby gem. There IS a java port out there I think, and a PHP one. But it has its roots in Ruby, not Java.

Nice story overall though, thanks for the exposure!

Wayne Brantley said...

Woops...thanks for correction. ;-)

Hainesy said...

I've been surfing around this afternoon looking for some best-practice/workflow to help us with our ever-increasing css bloat.

Just stumbled upon your solution and can't wait to give it a go.

Anonymous said...

I can't get this to work. My config contents are below. I get an error stating 'System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.'

Wayne Brantley said...

@Anonymous: The plugin has a list of files to watch and related config files, so when they change it can rebuild the style sheet. I do not see your config file, did not post to blog I guess?

Do you have multiple config files? This is supported - but not tested much. I will be glad to help find this bug if want to help?

Anonymous said...

Great Article,

Thanks for this, I want to implement it on my current project.

Rajnish Sharma
http://ignou-student.blogspot.com

North-Courier said...

I just installed it, and I get an error screen when I try to save the .less.css file:

Dotless: Exception was thrown
at DotLess.AddIn.VSProjectItemManager.Process()
at DotLess.AddIn.VisProjectManager.Dispose()
at DotLess.AddIn.DotLessAddIn.GenerateCssFromLess(ProjectItem projectItem)

North-Courier said...

I have an updated version of VS 2008 and just tried dropping .less into my site, but when I save a .less file, I get the following error message:

DotLess: Exception was thrown
System.NullReferenceException: Object reference not set to an instance of an object.
at DotLessAddin.VSProjectItemManager.Process()
at DotLessAddin.VSProjectItemManager.Dispose()
at DotLessAddin.VSProjectItemManager.GenerateCSSFromLess(ProjectItem projectItem)


What am I doing wrong?

Anonymous said...

Hey,
Thank you for sharing.

Will implement it ... :)

Wayne Brantley said...

North-Courier,
Sounds like you have a problem with you .less file. Try just renaming a regular .css file (with no less options in it) and see if it works properly. If so, add your less options in until you see what the problem is. Additionally, I am going to try to publish the source in the next week or so.

North-Courier said...

Found why it bombs with this error:

DotLess: Exception was thrown
System.NullReferenceException: Object reference not set to an instance of an object...

It does not work on file based web sites, it works on web apps.

Thomas de Wit said...

While splitting up the less.css file, we want to keep the variables in one place. We've created an variables.less.css, and we're trying to include that in other less.css file using @import. Only problem is, it does'nt work. The whole @import seem to have no effect.

Any ideas?

Wayne Brantley said...

@Thomas - The version of less I am including is older and does not have the imports. I attempted to update to the latest today, but they changed the API. I fixed that and then there were problems building.

Anyway, I will get it fixed shortly and republish the updates along with all the source.

aa said...

Have it working in VS2010 (just add an entry to the addin file :)) Works perfectly...nice one! You can also add the less extension for VS2010 and get syntax coloring :)

Hainesy said...

Thought I'd weigh in again with my thoughts in case it helps anyone. Whilst I love what you've done here, sadly it doesn't work for our workflow - rebuilding a website after every stylesheet change isn't practical. Our front-end development workflow requires something that automatically shows the stylesheet changes in the browser without having to rebuild. I'm currently trialing my own customised version of CrusherModule from talifun-web, and am impressed with the results so far.