Building CSS sprites with Bash & Imagemagick

3/05/2010

I’ve been rebuilding the Crooked Tongues forum and one of the things we’ve been mindful of is trying to stick to some good practises with regards optimzing the site for fast loading. One of the easy ways to decrease your page load times is to use CSS sprites. This is just stiching together all your common images into one big one and then using background positions & width/heights in CSS to offset and “crop” the big image. This way you only get the one HTTP request made to request it.

If you’ve got hundreds of small images it can really speed up your page. A prime candidate for sprites are typically your buttons, header images and in the case of a forum, emoticons. Crooked uses lots of custom designed icons and when writing them out as individual images for the post toolbar really slowed down the page. There was no chance I was going to do it manually so I turned to a stable of my web development toolset – Imagemagicks convert.

Convert can do a lot. I’ll show you two such ways it made my life easier and cut down on the time I had to spend doing grunt work. First, here’s how the toolbar looks:

I wrote some bash to basically loop over a folder of images and then using convert to pull the width & height info, I use that to write the CSS file. Finally I loop over the images once more using the append command to output one big, long image. (gist)

#!/bin/bash
 
# uses imagemagick to stich together all images in a folder and
# then writes a css file with the correct offsets along with a
# test html page for verification that its all good
 
if [ $# -gt 0 ]
then
 
    if [ $3 ]
    then
        ext="."$3; # the extension to iterate over for input files
    else
        ext=".gif"; # the extension to iterate over for input files
    fi
 
    name=$1; # output will be placed in a folder named this
 
    if [ $2 ]
    then
        classname=$2"-sprite";
    else
        classname=$1"-sprite";
    fi
    css="$name/$classname.css";
    html="$name/test.html";
 
    rm -fr $name;
    mkdir $name;
    touch $css $html;
 
    echo "Generating sprite file...";
    convert *$ext -append $name/$classname$ext;
    echo "Sprite complete! - Creating css & test output...";
 
    echo -e "<html>\n<head>\n\t<link rel=\"stylesheet\" href=\"`basename $css`\" />\n</head>\n<body>\n\t<h1>Sprite test page</h1>\n" >> $html
 
    echo -e ".$classname {\n\tbackground:url('$classname$ext') no-repeat top left; display:inline-block;\n}" >> $css;
    counter=0;
    offset=0;
    for file in *$ext
    do
        width=`identify -format "%[fx:w]" "$file"`;
        height=`identify -format "%[fx:h]" "$file"`;
        idname=`basename "$file" $ext`;
        clean=${idname// /-}
        echo ".$classname#$clean {" >> $css;
        echo -e "\tbackground-position:0 -${offset}px;" >> $css;
        echo -e "\twidth: ${width}px;" >> $css;
        echo -e "\theight: ${height}px;\n}" >> $css;
 
        echo -e "<a href=\"#\" class=\"$classname\" id=\"$clean\"></a>\n" >> $html;
 
        let offset+=$height;
        let counter+=1;
        echo -e "\t#$counter done";
    done
 
    echo -e "<h2>Full sprite:</h2>\n<img src=\"$classname$ext\" />" >> $html;
    echo -e "</body>\n</html>" >> $html;
 
    echo -e "\nComplete! - $counter sprites created, css written & test page output. ~jaymz";
 
else
 
    echo -e "There should be at least 1 argument!\n\tbuildSprite.sh output_folder classname input_extension"
 
fi

The script outputs the names incrementing by one, I’ve gone over myself and turned them into “nice” names. All in all its taken maybe 10 minutes to convert it all into a sprite and nice CSS file. If you were using a primary key field like 1, 2, 3… etc then you could probably fudge it to not even have to name the id’s “nicely”.

Imagemagicks convert can also do some modification of your files. It can for example convert your image to a black & white copy. It’s not a great deal of work to then loop over a set of images, create b&w versions in a temporary folder and then stitch the two versions together to get a color→b/w sprite for hovers. I’ve actually seen people do this manually and spend an age at it, with this method you output them once then sit back for the 30 seconds or so whilst the computer does the work.

for file in *.jpg; do convert -colorspace Gray $file bw/$file; done
for file in *.jpg; do convert $file bw/$file -append sprites/$file; done

It’s times like this that I am reminded of just how much time you can save with a little shell script and the right command line tools. Not everthing needs point & click.

1 Comment

Zen in Kate: Getting zencoding & lessCSS as filters in Kate

3/05/2010

There’s been a fair amount of buzz surrounding the Zen Coding plugin’s for various editors. In one line, it lets you write out HTML via css selector syntax. So something like

table#calendar.clear>tr#week-$.week*4>td#day-$.day[valign=top]*7

Would transform into the appropiate HTML table:

<table id="calendar" class="clear" cellspacing="0">
	<tr id="week-1" class="week">
		<td id="day-1" class="day" valign="top"></td>
		<td id="day-2" class="day" valign="top"></td>
		<td id="day-3" class="day" valign="top"></td>
		<td id="day-4" class="day" valign="top"></td>
		<td id="day-5" class="day" valign="top"></td>
		<td id="day-6" class="day" valign="top"></td>
		<td id="day-7" class="day" valign="top"></td>
	</tr>
	<!--- ETC... 3 more rows of class week ---></tbody>
</table>

I use KDE's Kate editor for my main dev setup. I'm really used to its setup and find it a powerful tool for coding web apps. It's minimal compared to a full on IDE but I'm one of those developers that doesn't really like the whole IDE concept. For web development at least, if I was building on OS-X or Windows I'd probably be using Xcode or Visual Studio but for web-dev, I find Kate + a shell + Firefox with Firebug/Webdev/HttpFox is more than enough.

Unfortunately there isn't a plugin to give native Zen Coding support in Kate. Some googling found me this obscure reference to a "Insane HTML plugin" for the development trunk of Kate but when I checked out the git repo and compiled it I couldn't find it. Then I came across Sparkup. It's a similar thing to Zen (its based of its syntax and is compatible) and they provide it as a python script. Kate provides you the means to pipe text through a shell command, I use this to format or parse text through awk & sed quite a bit and so dropping in the sparkup python script to /usr/local/bin I could write out some CSS→HTML markup and just pipe it through.

It's not quite as slick as having a single hit plugin but it works enough to make it part of my toolchain now when creating templates. I've also been looking at things like Less CSS for adding variables and calculations to CSS files. You write your CSS in a more programming style markup (colors assigned to variables for example) and then Less compiles it out to actual CSS. I wanted to do something similar for sparkup for less so installed the ruby gem for it. Alas it takes a file as input and gives a file as output so I couldn't just pipe through selected text in Kate to lessc. Fortunately it only takes a little bit of bash to allow me to write abbreviated CSS, pipe it through lessc and have Kate drop back in the "compiled" version. Here's the bash (gist):

#!/bin/bash
input=''
while read data
do
	input=${input}${data}
done
echo $input > /tmp/ccss.tmp.less
/var/lib/gems/1.9.1/bin/lessc /tmp/ccss.tmp.less /tmp/ccss.css
cat /tmp/ccss.css

All it's doing is putting the input into a variable, outputting that to a file and then calling lessc on it. The output is then cat'd back to standard output which means Kate will dump it out over our selection. Combining these two gives Kate a fair bit more power as my main (pretty much only) coding environment, and means I don't have to switch out to use something else to give zen/less css a go. I quite like the concept of Less CSS, for complicated projects using it from the outset would be a pretty good idea I think, for now I'm using it as a sort of shorthand. Your path to the lessc binary might be different but probably not by a lot.

Finally, the default binding for "pipe text to shell command" is CTRL+|, which, as I tend to use the left control key makes it a little constrictive to fluidly type. I also tend to end up hitting z or a instead which gets annoying so I remapped it to CTRL+#, which is a lot easier to hit with both hands fully extended. I think its a better mapping if you use it a lot. Which you will when you look up a few sed 1 liners :)

2 Comments