CSS Preprocessors – Sass, Less, Stylus

By eidias on (tags: css, less, sass, stylus, categories: code, web)

CSS has been around for a while and will stay for a while longer and although I think it’s a good and working ‘language’ it has a few pitfalls – the biggest one being the lack of ability to reuse certain parts. Take colors for example, when you want to style a site you need to repeat the color declaration everywhere you want to set it, so when you change your mind, the only way to implement the changes is search and replace. Now that’s not that bad when you have a small site and one css file, but when the number of files grows, that problem grows along. So what can we do?

TL;DR;

This will be lengthy, so if you don’t have time, here’s the punch line:

If you’re using .net and want to use a css preprocessor use less along with dotless lib. For anything else, use stylus.

Resuming

Well, for one, we can wait for things like variables being included in css itself – the working draft for css3 does have variables in them, but (at the time of writing this article) none of the browsers support it (webkit has it in nightly builds… I think).
But variables are just one of the things that could ease the front-end developers life. Things like mixins (reusable chunks of css), improved imports (so, a declaration that will actually import a while file inline instead of sending another request to fetch the imported file), functions, calculations and who knows what else.

Come forth CSS preprocessors. They’ve been around for a while now, and are getting more attention. That may be because it is now easy to use custom parsers/compilers for css and javascript. So let’s take a look at the options we have.

Less

The project can be found here. Features that are highlighted on the project page are variables, mixins, nested rules and functions & operations. The nice thing about it is that it utilizes the current css syntax so it’s easy to learn but the mixins could use an improvement (it’s not possible to use a node as a mixin – so section { margin-bottom: 1em; } cannot be a mixin whereas .section { margin-bottom: 1em; } can).

Sass

Another option is sass. Project page can be found here. This one is the most popular one (at least it seems so). Couple of new syntax features – $ for variable, @mixin for mixins…). Supports pretty much everything that less supports and more. There are 2 features that caught my attention

  • @extend – which allows to reuse blocks – this allows to achieve the same effect as mixins but in a different way – kind of like inheritance in C# that’s the closest comparison I can think of
  • interpolation – you can do things like “{foo}-bar-buz" in property declarations and the variable inside of curly braces will be substituted for its value. Here is a closer look.

Stylus

This is the most feature rich preprocessor. Project site is here. It has a bunch of nice features, the syntax is flexible, so people who’d like to stick to the ‘original’ css syntax will be happy (that’s me) as well as people who think css is to verbose. It has everything – variables, mixins, operations, @extend, interpolation and a lot more. The only place where it doesn’t exceed the previous ones is color functions (sass has more) but that can be changed if you’re using nib (that’s an add-on package for stylus). On top of that it has a nice web page – which in my opinion, for a css preprocessor is kind of nice and sends a good signal.

Which one then?

First I’d like to point out an important and nice feature that all of them have – original css is a valid syntax for any of these preprocessors. That may come in handy especially when migrating existing sites to new styling methods.

If you’re interested in syntax and feature comparison here’s a nice article.

First – I wanted to check what kind of gain (apart from the reusability and better maintenance) would I get from this – I’m talking about LOC. There are online preprocessors available for less and sass. I haven’t found one for stylus. I took one of our projects stylesheets ran them through a css ‘optimizer’ so that the starting point was a common one and then ran it through a css to * converter. The original file was 272 lines. After automatic (note that this doesn’t really produce optimal code, but still does tell you something) conversion the result was:

  • less: 238 LOC
  • sass: 218 LOC
  • stylus: 225 LOC (by running it locally)

So that’s automated stuff, but… what if I wrote it by hand?

These are down and dirty ports. This means, that if I were to write it from scratch, it would probably be a bit cleaner and shorter, but for the sake of testing, this should be enough.

What we can see here is that not only the difference between these (in terms of number of lines) is insignificant, but it’s also insignificant compared to the original css. So what’s the gain? Of course, the other aspect mentioned at the very beginning – maintainability.

If the length is insignificant, and the syntax is very similar then from the descriptions, the choice should be obvious – stylus, then sass, then less. But that’s not as simple as it seems. In the .net web community it looks like less is taking the lead. Why is that? The quick research shows that it’s the least functional one and doesn’t really have any advantages over the remaining ones. So.. what’s up with that? Well…

How to use a preprocessor in a .net environment

Stylus

This thing is only available for node. That means that it’s a javascript lib and that means I can use it directly on the client! No. I can’t. My conscience won’t let me. It’ just a BAD idea. That goes for the rest of the preprocessors as well. So, what do I do to make it work in .net?

   1: string StylusToCss(string text, bool useNib = true)
   2: {
   3:     var workspace = System.IO.Path.GetFullPath(@"d:\code\personal\Flair\Flair\Node");
   4:     var executor = new NodeAssets.Core.Commands.NodeExecutor(workspace);
   5:  
   6:     var execCmd = ScriptFinder.GetScript("NodeAssets.Core.Compilers.Scripts.stylus.js");
   7:  
   8:  
   9:     var script = execCmd
  10:         .Replace("{0}", useNib ? "true" : "false")
  11:         .Replace("{1}", string.Empty);
  12:  
  13:     var command = executor.ExecuteJsScript(script);
  14:     command.StdIn.Write(text);
  15:     command.StdIn.Flush();
  16:     command.StdIn.Close();
  17:  
  18:     return executor.RunCommand(command);
  19: }

So what this does is it runs a node command on the server through a .net wrapper. Didn’t find another way. Too bad.

Sass

Is sass any better? Kind of. To run it do the following:

  • install SassAndCoffee.Ruby – still has a bunch of dependencies including IronRuby
  • use this code:

   1: string SassToCss(string text)
   2: {
   3:     var pal = new ResourceRedirectionPlatformAdaptationLayer();
   4:     var srs = new ScriptRuntimeSetup()
   5:     {
   6:         HostType = typeof(SassCompilerScriptHost),
   7:         HostArguments = new List<object>() { pal },
   8:     };
   9:     srs.AddRubySetup();
  10:     var runtime = Ruby.CreateRuntime(srs);
  11:     var engine = runtime.GetRubyEngine();
  12:  
  13:     // NB: 'R:\{345ED29D-C275-4C64-8372-65B06E54F5A7}' is a garbage path that the PAL override will
  14:     // detect and attempt to find via an embedded Resource file
  15:     engine.SetSearchPaths(new List<string>() {
  16:             @"R:\{345ED29D-C275-4C64-8372-65B06E54F5A7}\lib\ironruby",
  17:             @"R:\{345ED29D-C275-4C64-8372-65B06E54F5A7}\lib\ruby\1.9.1" });
  18:  
  19:     var source = engine.CreateScriptSourceFromString(
  20:     Utility.ResourceAsString("lib.sass_in_one.rb", typeof(SassCompiler)), SourceCodeKind.File);
  21:     var scope = engine.CreateScope();
  22:     source.Execute(scope);
  23:  
  24:     var sassCompiler = scope.Engine.Runtime.Globals.GetVariable("Sass");
  25:     var sassOption = engine.Execute("{:cache => false, :syntax => :sass}");
  26:     var scssOption = engine.Execute("{:cache => false, :syntax => :scss}");
  27:  
  28:     try
  29:     {
  30:         return (string)sassCompiler.compile(text, scssOption);
  31:     }
  32:     catch (Exception ex)
  33:     {
  34:         // Provide more information for SassSyntaxErrors
  35:         if (ex.Message == "Sass::SyntaxError")
  36:         {
  37:             dynamic error = ex;
  38:             StringBuilder sb = new StringBuilder();
  39:             sb.AppendFormat("{0}\n\n", error.to_s());
  40:             //sb.AppendFormat("Backtrace:\n{0}\n\n", error.sass_backtrace_str(pathInfo.FullName) ?? "");
  41:             //sb.AppendFormat("FileName: {0}\n\n", error.sass_filename() ?? pathInfo.FullName);
  42:             sb.AppendFormat("MixIn: {0}\n\n", error.sass_mixin() ?? "");
  43:             sb.AppendFormat("Line Number: {0}\n\n", error.sass_line() ?? "");
  44:             sb.AppendFormat("Sass Template:\n{0}\n\n", error.sass_template ?? "");
  45:             throw new Exception(sb.ToString(), ex);
  46:         }
  47:         else
  48:         {
  49:             throw;
  50:         }
  51:     }
  52: }

This thing runs a ruby package command using a .net wrapper. So…kind of better but still sucks.

Less

Any luck here? Yes. Do this:

  • install dotless – no dependencies
  • use this code:

   1: string LessToCss(string text)
   2: {
   3:     return dotless.Core.Less.Parse(text);
   4: }

This thing runs a dedicated .net library for handling less. I think we have a winner, although I must say, that I feel kind of sad to use the least capable preprocessor just because it’s easy to use it. On the other hand, I could be using plain old css so… let’s go for less (no pun intended). If only for the sake of having variables – it’s worth it.

Is that all?

Just one thing remaining. We have a way to generate css from something (that is less in our case) but now what?.  Both Sass and Stylus (technically NodeAssets) has a package for AspNet. So why not use these? Well, the mentioned packages were created prior to asp.net 4.5 – so if you’re using anything older than that, go for the AspNet packages. But if you are using the newest version then you probably know of a new feature called bundling and minification. That thing allows the programmer to bundle up (combine) and minify stylesheets and javascript files and it also allows to use custom preprocessors.  The usage is quite simple – extend a base class, add preprocessing, done. Here’s some code from John Papa:

   1: // somewhere in your project
   2: public class LessTransform : IBundleTransform
   3: {
   4:     public void Process(BundleContext context, BundleResponse response)
   5:     {
   6:         response.Content = dotless.Core.Less.Parse(response.Content);
   7:         response.ContentType = "text/css";
   8:     }
   9: }
  10:  
  11: // in BundleConfig.cs. LessTransform is ours, CssMinify comes from web optimizations - included in mvc 4
  12: bundles.Add(new Bundle("~/Content/css", new LessTransform(), new CssMinify()).Include("~/Content/site.less"));

Cheers,

Chris