robert hahn

a darn good web developer

June 24, 2006

CSS Preprocessor – Take 2

In my first article on building a CSS preprocessor, I made an initial cut at adding DRY development techniques to CSS files by leveraging cpp as the preprocessor. Initial feedback on the solution has encouraged me to rethink the problem by using gm4 as the preprocessor instead of cpp.

The reason for this is obvious, and I hadn’t discovered it until close to finishing the previous article – cpp’s macro syntax requires the use of #, which is the same character used for id selectors. oops.

One of the nice things about the architecture I proposed is that it’s super easy to swap out the C preprocessor for gm4launchd didn’t need to be touched, gen_css.sh had to be rewritten to use the gm4 command, and even the defines were easy to convert. So, without further ado, let me show you what’s changed – the actual examples will remain the same as from the previous article.

Using gm4

GNU m4 is, well, the GNU re-implementation of the m4 macro processor. And, turning again to the wikipedia, a macro processor is

… a text-replacement tool. Its chief use is to re-use text templates, typically in programming applications, but also in text editing, text processing applications.

Let’s see what’s changed in my styles.csse file:

styles.csse

m4_include(`includes.m4')

div {
  _no_margins_n_paddings;
  border-color: 1px solid _red;
  background-color: _tan;
}

#foo {
  width: 1px;
}

And the answer is… nothing! Well, nearly nothing. First off, the way I include the new m4 definitions has changed a bit, but that’s just syntactic sugar. The macros themselves didn’t need to change at all (yay!), and #foo didn’t need to be written as id(foo) (w00t!)

Now let’s look at the includes.m4 file:

includes.m4

m4_define(_red, `#f00')
m4_define(_tan, `#cc8')
m4_define(_no_margins_n_paddings, `margin: 0; padding: 0')
m4_define(_m_and_p, `margin: $1; padding: $2')

Looks substantially similar here too - just a bit of syntactic sugar is all that’s changed. As long as you’re not doing anything more complicated than what I’ve used here, you should be able to do just fine with the examples above. However, if you do want to try fancier stuff, gm4 is quite up to the task; you just have to be prepared for a steep learning curve. Here is the gm4 manual

To parse styles.csse here, you need to execute this on the command line:

gm4 -P styles.csse >styles.css

Breaking down that command:

Processing Many CSS Files at Once

This quite naturally leads us to the new gen_css.sh file, which has the updated command invocation:

gen_css.sh

#! /bin/sh

gm4 -P styles.csse >styles.css
gm4 -P test.csse >test.css

exit 0

It Can’t be That Easy, can it?

Actually, yes. As long as you either stick to the simple cases I outlined here when using gm4, or you take the time and effort to learn it properly, this really is about as caveat free as you can get.

Wrap-up

I would probably want to use this gm4 version instead of the cpp version mentioned yesterday - it works much better with CSS files, and is a much more powerful language. If you wanted to use arithmetic, for example, to somehow define one macro as being 0.1ems less than whatever the other macro is, you could probably do that. I don’t know how, as you’d have to split off the units from the numbers, and you’ll probably get some weird things happening if you want to take 0.1em away from 12pts, but it does seem to be possible. I would probably prefer to stick with the simple cases, as it’ll Just Work 90% of the time, and a little repetition in the .m4 file is still going to save me lots of effort in the CSS file.

I think that between this article, and the previous one, you should have enough to get you going. I’m open to writing up some New Improved documentation that would go in a bit more detail about how to get this working if there’s call for it.

Thanks again to Daniel Read for inspiring me to work on this problem. I’m pretty happy about how this one turned out so far. Enjoy!

decorative image of trees

Copyright © 2009
Robert Hahn.
All Rights Reserved unless otherwise indicated.