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.

Update on Jan, 5 2011: Corrected code typos and added a live demo page. Corrected some curly braces that were point­ing the wrong way, and updated ven­dor pre­fix prop­erty ref­er­ences, so -webkit and -moz are listed before the non-prefixed property.

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 {

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

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

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. (Be sure to spec­ify the vendor-specific pre­fixed prop­er­ties first. For clar­ity, I’ll omit the ven­dor pre­fixes 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:active {

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:active {

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.

See the live demo!

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!

5 comments. »

  1. Hi,

    I don’t think this works. I’ve tried and got no good results.

    I’ve used a sim­u­la­tor to test it, here is the links.

    Green but­ton with­out sprites: http://bit.ly/gMra6W

    Yellow but­ton with­out sprites: http://bit.ly/g2P3Hw

    Tentative with your sprite image: http://bit.ly/dWsdsn

    This URLs will will use your hosted images, no tricks. Someone pointed out this is impos­si­ble in stack­over­flow: http://stackoverflow.com/questions/3191292/webkit-border-image-with-css-sprites

    Comment by Iraê — 2011 January 05 @ 6:39 am

  2. You’re totally right! I should have used the :active pseudo­class, not the .pressed class, which was left­over from another project. Sorry about that. I’ve cor­rected the code above, and added a live demo page.

    Comment by Scott — 2011 January 05 @ 11:29 am

  3. I’m sorry for my mis­take! It really works.

    The trick is to com­bine it with the border-width attribute. Thanks for includ­ing the demo.

    Just for the record, I also was able to use the boder image gen­er­a­tor now that this is clear. Using sprite, green but­ton: http://bit.ly/ihZhmV

    Using sprite, yel­low but­ton: http://bit.ly/f1qEs8

    Thank you again for the post update!

    Comment by Iraê — 2011 January 05 @ 11:48 am

  4. Unfortunately this is not 100% a solu­tion. I think one of the points and big advan­tages of border-image is that the result is scal­able (both hori­zon­ally and ver­ti­call). It seems your solu­tion only really works when using vari­able width and breaks (or stretches) on vari­able height sce­nar­ios. Please cor­rect me if I’m wrong…

    Comment by Casey — 2011 December 09 @ 8:04 am

  5. @Casey — Yes, you’re right about that. Since I wrote this, browsers have rolled out greater sup­port for new bor­der styles and inter­nal and exter­nal box shad­ows. So I’d prob­a­bly rec­om­mend using these new CSS3 tech­niques to style your but­tons. If that’s not pos­si­ble, or con­sis­tency with older browsers is absolutely nec­es­sary, then you could use this technique.

    Comment by Scott — 2011 December 09 @ 8:16 am

RSS feed for comments on this post.

Leave a comment

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