Jeff Croft

I’m a product designer in Seattle, WA. I lead Design at a stealthy startup. I recently worked at Simply Measured, and previously co-founded Lendle.

Some of my past clients include Facebook, Microsoft, Yahoo!, and the University of Washington.

I’ve authored two books on web and interactive design and spoken at dozens of conferences around the world.

But seriously, who gives a shit?

Blog entry // 02.23.2012 // 9:23 AM // 18 Comments

The many ways to work with CSS preprocessors

There’s a fair amount of confusion surrounding CSS preprocessors like Sass and LESS, and I think some of it has to do with the fact that there are so many different ways you can use them. I thought I’d outline the different approaches, and some of the pros/cons to them.

  1. Local compilation: The idea here is that you write Sass/LESS code on your local Mac or PC, run a compiler that coverts it into standard CSS, and then treat that CSS file as if you’d written it directly — upload it to your server, put it in your version control repo, whatever. The compilation step can be done manually, or automatically. For example, Sass will watch your files in the background and compile them automatically as they change. Also, apps like CodeKit and Less.app put a pretty graphical front-end on the compilation process, if you prefer. The advantage to this approach is simplicity, especially for non-programmers. It also doesn’t impact your development workflow much and server configuration at all. The big disadvantage is that other team members never see your Sass/LESS code, only the resulting CSS, which means as soon as they make an edit, your Sass/LESS code is out of date, with no easy way for you to update it.

  2. Client-side compilation: In this case, you write, store, and serve to the browser Sass/LESS code instead of standard CSS. No CSS file is ever created and saved anywhere. Instead, the browser, using Javascript, compiles the Sass/LESS code on the fly, converting it to proper CSS. Again, the advantage here is simplicity. You can switch from writing CSS to writing Sass/LESS code, store that Sass/LESS code on your server and in your version control repository, drop a call to a Javascript file in your HTML, and bam, you’re done. Unfortunately, this method is terrible for performance. Rather than the Sass/LESS code being compiled once, it’s compiled on every single page request — and to make matters worse, you’re passing that burden off on your users, rather than doing it yourself. In my not-so-humble opinion, this method is always a terrible idea and I wish it didn’t exist. It’s just too tempting to shoot yourself in the foot.

  3. Compile-and-cache on request: With this method, the compilation of Sass/LESS code happens on the server side, when a new or changed file is detected. This is the method I, personally, am using. I store Sass/LESS code in my version control repository and on my server, and on the first request to a page, that code is compiled into standard CSS. The standard CSS is stored in a cache directory, and is used on subsequent requests. The advantage, here, is that developers need not ever see or concern themselves with the standard CSS — only the Sass/LESS code. Compilation happens automatically and you just don’t have to worry about it. All developers on a team can easily work on the same Sass/LESS files and store their changes in version control. The disadvantage is that it does slow does the first request to your site after a Sass/LESS file has been changed. It’s only the first request, though, that is slowed down, so it doesn’t bother me. Another disadvantage is that this method takes some setup on the server side to get it all working.

  4. Compile on deployment: Another option is to compile Sass/LESS code into CSS as part of the deployment process. Most modern web projects are deployed automatically with scripts (using tools like Capistrano and Fabric), so it’s no big deal to simply add one more line that compiles your Sass/LESS code as the code changes is being deployed. The compilation can happen server-side or client-side, although I’d say it makes a bit more sense server-side, as that ensures all developers on the team are compiling against the same version of Sass/LESS. The advantages are much the same as the compile-and-cache on request method: the developer doesn’t ever have to worry about standard CSS files, and it’s easy for multiple developers to work on the project. They simply focus on their Sass/LESS code, and when the project is deployed, the compilation happens automatically. I’m not aware of any real disadvantages to this method, other than the fact that it takes a little bit of setup to get it working.

There may be other approaches, as well. The flexibility of these tools is a strength of them, but it can also introduce a great deal of confusion. I’ve seen several articles and blog posts coming out against CSS preprocessors for disadvantages that only exist in one of these approaches (such as difficulties working with Sass/LESS files in teams). In an effort to make these tools appear as simple as possible, the Sass and LESS teams have put a lot of weight on the local compilation method in their tutorials, screencasts, etc. Because of this, many folks don’t realize the other methods exist, and that they alleviate many concerns they may have with CSS preprocessors.

What do you think? Are you using one of these methods? Do you have another method to share? Which do you think makes most sense in your workflow?

Comments

  1. 001 // Carl Meyer // 02.23.2012 // 9:37 AM

    You left out the option I prefer: local compilation, but commit BOTH the CSS and the Sass/Less, and make sure everyone on your team knows to never edit the CSS directly.

    Preferably the repo README also notes that the CSS is auto-generated, and includes simple instructions for how a new dev can install the preprocessor and regenerate the CSS from the Sass/Less.

    This erases the only disadvantage of the local compilation option.

  2. 002 // Trav // 02.23.2012 // 9:40 AM

    I use option 1, but keep the SASS files in tact on the git repo (usually outside of the web root) so other developers can still access them.

    Any reason why this would be a bad idea? Seems to work fine for me.

  3. 003 // Ben Bodien // 02.23.2012 // 9:42 AM

    One other thing to note is that if you’re using method 3 or 4, you’ll want to make sure your source control is set up to ignore the generated .css files, as you’ll only be worried about the Sass/LESS source files. For example, add *.css to your .gitignore if using Git.

    Totally with you on client-side compilation which should not be an option ever.

  4. 004 // Bill // 02.23.2012 // 9:46 AM

    Nice little write up. Do you have any suggested reading for Option #3? It’d be especially helpful if tailored to IIS, but I’ll take anything.

  5. 005 // Jeff Croft // 02.23.2012 // 10:07 AM

    Carl/Trav: I think that method works fine, as long as it’s very clear which files to edit (Sass/LESS) and which to not touch (CSS). I’d be concerned about developers editing the wrong files, but if that’s not an issue, then I think that’s a perfectly valid method. Something feels ugly and inelegant to me about committing the CSS to version control, but I can’t really justify that feeling rationally. :)

    Ben: Good point!

    Bill: I don’t, myself (I use a Django tool called django-compressor to handle this), but hopefully someone else will.

  6. 006 // Matt Wiebe // 02.23.2012 // 10:16 AM

    I, like Carl and Trav, commit both. It’s the easiest transition out of working with straight CSS - no extra server bits to set up, but you’re still working, collaborating in the Sass/LESS file. We put a warning comment that reminds the developers that the CSS is generated from the Sass file, which is where changes should be made and then compiled.

  7. 007 // Matthias Dietrich // 02.23.2012 // 10:28 AM

    We’re using the Javascript LESS compiler on our dev machines and “precompile” the LESS on our testing and live machines. The resulting CSS will never see our repo or the site itself. It’s compiled, packed (there are some more CSS @imports, eg. of other libs), gzipped and uploaded to our CDN on the fly. Like that workflow since the devs can apply changes and test them without compiling the LESS first. :)

  8. 008 // Arnoud ten Hoedt // 02.23.2012 // 10:56 AM

    The major thing I see missing (and not just in this topic) is the usage of continuous integration as optimization for the #4 option. There isn’t really any reason nowadays why you wouldn’t want to run your compilations and tests on each and every commit even when you are doing “just” CSS.

  9. 009 // Harlan Lewis // 02.23.2012 // 11:38 AM

    Great writeup! Local compilation is what I use for personal projects, and I agree with the comments above - it’s by far the easiest thing to do if you’re confident folks know how it all works. But I’d never dream of that approach on bigger projects.

    I’ve worked with Sass on some pretty big projects (many tens of thousands of Sass lines spanning lots of apps & sites with tangled webs of interdependencies, teams of 2 to 30, over the course of 3+ years). I’ve been stung by overconfidence in documentation and process, exacerbated by all the problems that come with manually handling compiled assets. Configuration that reduces the ways mistakes can be made is a very worthwhile investment that will free your future brain for thinking about more interesting problems.

    Why not local compile? Committing both Sass and compiled CSS introduces a dependence on documentation and training - these will inevitably be ignored, skipped, or missed at some point. Manually checking in compiled Sass to source control and production allows an opportunity for the worst-case scenario: manually-edited CSS at a different state than its owner Sass, propagated to other devs via the repo and potentially pushed all the way to production. The next time Sass is compiled your team loses work.

    Someone always directly edits local compiled CSS. This is a problem that hasn’t gone away despite rising familiarity with Sass. Acknowledging this, dev environments should be configured with a few goals in mind: 1) prevent direct edits to compiled assets, 2) reduce the feedback loop for the developer so mistakes are recognized faster, 3) prevent direct edits from reaching other developers, and 4) prevent direct edits from reaching the production site.

    With that in mind, here’s the system I use now.

    The complete training for all developers: never edit .css files directly. (as with all documentation, this will eventually be skipped or ignored).

    dev (local) - Sass referenced by page is automatically compiled on localhost page request. Compiled CSS is never committed or pushed (.gitignore).

    The feedback loop is tiny - if you change a compiled CSS file and reload the page, it will instantly be clear changes weren’t applied. At this point a smart dev will spend a couple minutes tinkering, ask someone else to take a look, and get a friendly reminder to only edit Sass files.

    production (live) - On deploy, all Sass is compiled anew. If any direct edits somehow made it to the repo (persistent devs can sometimes be too clever, especially if they skip asking for help), the direct edits will be ignored & the sanctity of Sass/CSS relationships will be preserved.

    Goals 2, 3, and 4 are all satisfied. If you know of a way to prevent direct edits to compiled assets, I’m all ears!

  10. 010 // Nathan // 02.23.2012 // 1 PM

    Great overview but local compilation can work wonderfully with teams. Commit your Sass/LESS, have the team edit it, and compile locally also.

  11. 011 // Nathan McGinness // 02.23.2012 // 1:57 PM

    My preferred workflow below:

    1. Compile locally to avoid environment dependencies (this workflow should work on all projects and stacks).

    2. Have a Sass/LESS folder, and a CSS folder (or even better, roll all your Sass/LESS one minified CSS file that powers your site - easy to do with Sass).

    3. Have your team work only on your Sass/Less files, and also compile locally. It’s important they understand that editing .css files is fruitless (will be overwritten). You can basically ignore conflicts in .css and pay attention to getting your Sass/Less beautiful.

    I think this workflow is better suited to small teams than massive enterprise applications but my favorite defense so far against people editing .css directly is to minify/compress it. Editing you’re beautifully laid out .scss is going to look far more appealing.

  12. 012 // Seth // 02.23.2012 // 1:59 PM

    How would one go about setting up the server for server side compiling and caching of sass/scss projects?

  13. 013 // Brandon Rhodes // 02.23.2012 // 2:05 PM

    Like Carl/Trav I commit both my SCSS and CSS to version control. I find this especially important since new Compass versions come out, and when a new Compass version comes out I can process my old SCSS with it, and my version-control system will show me whether the CSS produced looks the same as with the old Compass version or whether a mixin change is now producing slightly different CSS. I can review the improvement it thinks it is making, test it against some browsers, and decide whether to adjust my SCSS to compensate.

    Another note: it does not need to be a disaster if someone hand-edits the CSS in the repository. In fact, this is normal if only a few hard-core designers are writing the SCSS but lots of lesser front-end guys might need to tweak the CSS a bit. No problem: as their branch is integrated, a real SCSS guy can review what they did in the CSS and then reproduce it correctly in the SCSS.

  14. 014 // Masklinn // 02.23.2012 // 11:48 PM

    Something feels ugly and inelegant to me about committing the CSS to version control, but I can’t really justify that feeling rationally. :)

    • There’s the risk (which still exists) of somebody editing the CSS file and not the generator, so some sort of process to ensure backporting of changes is required (see: Brandon Rhodes’s case)

    • Lots of churn in the generated file as very small changes in the source can lead to complete rewriting of the destination (even more so when using aggressive optimization/minimization filters), the committed generated file essentially acts as a cache except the burden of keeping the cache in the correct state is the developer’s. It also generates needless conflicts, even if those can be easily reverted that’s still increased ground for mistake

    • It’s not needed, with a bit of infrastructural filters (most of which you detailed)

    Do you store the project’s sqlite, mysql or postgres “empty” database in VCS? Or the generated object and binary file output of your compiler for a C or C++ project? Probably not, you’ll store the least necessary things allowing you to get the project working. CSS output of a preprocessor is the same, it’s a generated artifact. Even more so when there are tools available which don’t require a dynamic build step (e.g. django-compressor or Rails’s assets pipeline)

    Although I have to admit Brandon Rhodes’s point about reviewing generator output after updates to the generator or its libraries is interesting. Even if that implies always committing “readable” versions of the generated output.

  15. 015 // Kuzeko // 02.24.2012 // 1:29 AM

    In option 3, what if we put in place a “script” that after deploy performs itself the first request, is this a good solution?

  16. 016 // Arnoud ten Hoedt // 02.24.2012 // 5:20 AM

    @KUZEKO,

    That is basically preheating your website, which is a practice you can employ regardless of the usage of css preprocessors. Preheating your servers to prefill caches, precompile JSP files as part of your rolling deployment in general is a good practice.

  17. 017 // glenn // 02.24.2012 // 12:52 PM

    We compile locally and check in both the .less and the resultant .css files, as carl mentions in the first reply.

    We prefix these files with “less_”. That way if we see a less_something.css file, we know to only work in the corresponding .less file.

  18. 018 // temporalcube // 03.09.2012 // 6:02 PM

    I also commit both the SCSS and CSS to version control like Carl/Trav. I’m not overly worried about someone altering the CSS file instead of the SCSS since changes can be adapted if it happens, but more importantly, it isn’t much of an issue if team communication is done as well as it should be.

Tags for this entry
Links in this entry