Using CSS3 border-image Sprites for Flexible Button States

2010 July 15

Much has been writ­ten about CSS sprites, but less so about CSS3’s border-image. I wanted to use the two together to pro­duce beau­ti­ful, scal­able but­tons that use only one image for both nor­mal and active (e.g. clicked) states. I couldn’t find any­one else online using this approach yet, so I doc­u­mented my exper­i­ments here.

First, here is the amaz­ing but­ton that we want to create:

Its active state will be trig­gered on either mouse­Down or a touch event (for iOS and Android devices, say). In that case, we want the col­ors to change as shown:

Of course, ide­ally we could cre­ate this but­ton using pure CSS styles, like border-radius and box-shadow, or with SVG vec­tor art. But let’s say that for what­ever rea­son, this but­ton has visual ele­ments that can’t be cre­ated with CSS alone, and we have to use a bitmap image. (All images on this page are PNGs with alpha transparency.)

CSS3’s new border-image prop­erty lets us spec­ify an image to stretch to fit our but­ton, no mat­ter how wide or tall it becomes. Usually, border-image is used with an image like this:

This image is 40 pix­els wide by 50 tall. So we could use this CSS:

#buttonElement {

border-image: url(../images/button.png) 0 18 0 18;

-moz-border-image: url(../images/button.png) 0 18 0 18;

-webkit-border-image: url(../images/button.png) 0 18 0 18;

{

This should grab 18 pix­els from either side of the image and stretch it to fit. Of course, we’d then have to over­lay the but­ton text on top of this back­ground. (I always spec­ify both the “offi­cial” CSS3 prop­erty, plus the Mozilla and Webkit deriva­tions. For clar­ity, I’ll omit the lat­ter two below.)

The old-fashioned way to cre­ate the button’s active state is to gen­er­ate a sec­ond PNG:

…and style it with more CSS. Note that we’ve added the .pressed class, and we’re now ref­er­enc­ing a dif­fer­ent PNG:

#buttonElement.pressed {

border-image: url(../images/button_active.png) 0 18 0 18;

{

You would then use JavaScript/jQuery/etc. to add the .pressed class to #but­tonEle­ment when­ever it is clicked or tapped.

But by using the CSS sprites tech­nique, we can include both but­ton states in a sin­gle image. (This one is 40 pix­els by 100.)

Then, we mod­ify the border-image para­me­ters to ref­er­ence dif­fer­ent parts of our sprite image, depend­ing on the but­ton state:

#buttonElement {

border-image: url(../images/button_sprite.png) 0 18 50 18;

{

#buttonElement.pressed {

border-image: url(../images/button_sprite.png) 50 18 0 18;

{

See those “50” val­ues? They are the key to shift­ing the border-image ref­er­ence up and down, as needed.

Now isn’t that bet­ter? Just one image means fewer net­work requests, plus the button’s active state is already pre­loaded into mem­ory — no need for extra weird JavaScript!

Site content and design © copyright 2006–2008 Scott Murray.