JavaScript - - By Troels Knak-Nielsen

Use Webkit and Imagemagick to Create Cross-browser Buttons and Other Swag

So you’ve seen the nice BonBon buttons and Super Awesome Buttons and you really want to use them in your web design. Only, as we all know, most people out there use Internet Explorer, which makes them render like crap.

For this tutorial, I’ll show how you can render the well known super awesome button into a sprite that can be used in most current browsers and look just as good as they do in webkit. You can use the technique for a lot of things, such as boxes, borders, and so on.

Before you start, you need to install webkit2png and imagemagick.

Now, let’s start out with a basic awesome button and save it as /tmp/awesome.html. I like the blue one:

<style>
a {
  background: #222 url(http://www.zurb.com/images/alert-overlay.png) repeat-x;
  display: inline-block;
  padding: 5px 10px 6px;
  color: #fff;
  text-decoration: none;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
  border-bottom: 1px solid rgba(0,0,0,0.25);
  cursor: pointer;
  font-size: 13px;
  font-weight: bold;
  line-height: 1;
  background-color: #ff5c00;
}
</style>
<a href="#">&nbsp;</a>

It will render as this:

Let’s just get some more width on this thing, as we’re going to cut it up later:

a {
  ...
  width: 240px;
}

That’s better:

Now, to render this into an image we could of course just take a screenshot. But that wouldn’t preserve the delicate alpha channels in the shadows. So let’s break out webkit2png:

webkit2png --transparent --output=./awesome.png file:///tmp/awesome.html

Note that you need to specify the absolute path to the input file as the second argument, using the file:// protocol.

This should give you a screen capture in awesome.png of 800×600 pixels. Let’s trim that down a bit, shall we? You need imagemagick installed for this:

convert -trim +repage awesome.png awesome.png

Et voila—the image is now 260×26 pixels. But we also need an image for the hover effect. So let’s just adjust the stylesheet a bit. This should do:

a {
  ...
  background-color: #f90;
}

Save it under a new name—/tmp/awesome-hover.html. Now repeat the process from above:

webkit2png --transparent --output=./awesome-hover.png file:///tmp/awesome-hover.html
convert -trim +repage awesome-hover.png awesome-hover.png

Next we glue the two pieces together to create our sprite. Imagemagick comes handy again:

montage awesome.png awesome-hover.png -background transparent -tile 1x2 -geometry +0+0 combined.png

So now we have the two states rendered into a single sprite. Alright, but we still need to create the css to use it. Let’s create a new document in awesome-sprite.html:

<style>
body { font-family: sans-serif; }
a {
  background: url(combined.png) no-repeat;
  display: inline-block;
  padding: 6px 10px 6px;
  color: #fff;
  text-decoration: none;
  text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
  cursor: pointer;
  font-size: 13px;
  font-weight: bold;
  line-height: 1;
  width: 240px;
}
a:hover {
  background-position: 0 -26px;
}
</style>
<a href="#">Awesome</a>

Note that I adjusted the padding to compensate for the border-bottom. That way the combined height will stay the same.

But wait. That’s nice and all, but this sprite is locked to a fixed width of 260px. What if we wanted to make it fluid? We can use the sliding doors technique for this:

<style>
body { font-family: sans-serif; }
a {
  background: url(combined.png) no-repeat;
  background-position: right 0;
  display: inline-block;
  padding-right: 10px;
}
a span {
  background: url(combined.png) no-repeat;
  background-position: left 0;
  display: inline-block;
  padding-top: 6px;
  padding-bottom: 6px;
  padding-left: 10px;
  color: #fff;
  text-decoration: none;
  text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
  cursor: pointer;
  font-size: 13px;
  font-weight: bold;
  line-height: 1;
}
a:hover { background-position: right -26px; }
a:hover span { background-position: left -26px; }
</style>
<a href="#"><span>Awesome</span></a>

I moved most of the styling into the inner span and left some padding to make room for the ends of the sprite. It’s close enough, but there’s still a problem:

Look at the sides of this thing!

Because our sprite is transparent, we can’t simply overlay the images like this. We need to do some surgery to the sprites. First let’s slice off the ends into a temporary image:

convert sprite.png -gravity west -crop 250x52+0+0 +repage left.png
convert sprite.png -gravity east -crop 10x52+0+0 -background transparent -extent 250x52 right.png

This cuts the sprite into a left side and a right side, where the continuous background is together with the left part.

And finally, let’s combine them back into one sprite:

montage left.png right.png -background transparent -tile 1x2 -geometry +0+0 sprite_final.png

We end up with a sprite that looks like this:

Then simply adjust the offsets on those backgrounds:

<style>
body { font-family: sans-serif; }
a {
  background: url(sprite_final.png) no-repeat;
  background-position: right 0;
  display: inline-block;
  padding-right: 10px;
}
a span {
  background: url(sprite_final.png) no-repeat;
  background-position: left 0;
  display: inline-block;
  padding-top: 6px;
  padding-bottom: 6px;
  padding-left: 10px;
  color: #fff;
  text-decoration: none;
  text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
  cursor: pointer;
  font-size: 13px;
  font-weight: bold;
  line-height: 1;
}
a:hover { background-position: right -26px; }
a:hover span { background-position: left -26px; }
</style>
<a href="#"><span>Awesome</span></a>

And there we are. Awesome indeed.

You can repeat this formula by stuffing it in a shell script and you have your own little button-factory. In fact, being Christmas and all, I did the work for you. Just save the snippet as webkit_sprite and make it executable (chmod +x webkit_sprite), and you can invoke it like this:

PADDING=10 HTML_NORMAL=normal.html HTML_HOVER=hover.html ./webkit_sprite
Sponsors