Ruby, Make and CSS
Most large CSS designs have a number of properties that are hard-coded in different places—colours, sizes, widths, paths to locations for static files, etc. Changing these values can be tedious. A simple find-and-replace can (a little inelegantly) allow you to do global changes for colours, but changing sizes can be a lot more awkward—you’ll often have values that differ from, but depend on, your “master” values (or different properties that through coincidence have the same value). Here, textual substitution won’t cut it.
To keep sane at Auctomatic, and to make experimentation with major changes in CSS easy, we use Ruby scripts and Makefiles as a preprocessing system to generate all of our CSS.
In the simplest setup, say you have a CSS file called main.css. We rename it to have a .ucss (Unbaked CSS) extension, and gradually integrate Ruby expressions with the existing CSS:
#foo {
font-size: 10pt;
margin-left: #{Width + 10}px;
background-image: url("#{StaticHost}/images/bg.png");
}
#bar {
float: left;
width: #{Width}px;
}
This isn’t just slightly better textual substitution. The fact that all of Ruby is available means that relations that were previously only implicit in the CSS can be made explicit in code. We’ve found that once you approach CSS like this, you need surprisingly few hard-coded values; mostly, constants can be readily derived from something else. The syntax is also unobtrusive enough that the files still work with syntax-highlighting in the CSS mode of most editors.
We then use a Ruby script, gen.rb, to evaluate the CSS files in a context that maps variables to their values:
Width = 200
StaticHost = "http://example.com"
puts eval("<<-EOF\n" + File.open(ARGV[0]).read + \
"\nEOF")
Lastly, we have a simple Makefile:
GEN=gen.rb RUBY=ruby SOURCES=main.css common.css test.css all: $(SOURCES) %.css: %.ucss $(GEN) $(RUBY) $(GEN) $< > $@.out && mv $@.out $@ clean: rm -f $(SOURCES) $(SOURCES:=.out)
Now, you can just run make every time your unbaked CSS files are modified.
This can be easily extended to multiple CSS files (all of which may share properties), and has plenty of knock-on benefits (such as that, by factoring out the semantically meaningful data from the dependent data, CSS plays much nicer with source control—changes that are simple don’t generate huge diffs).
There are lots of additional things you can do once you have this set-up in place (e.g. having dependent colour values auto-generated)—that’ll hopefully follow in a future blog post.
(Thanks to ralph on news.yc for feedback.)