Using CSS3 border-image Sprites for Flexible Button States
2010 July 15
Much has been written about CSS sprites, but less so about CSS3’s border-image. I wanted to use the two together to produce beautiful, scalable buttons that use only one image for both normal and active (e.g. clicked) states. I couldn’t find anyone else online using this approach yet, so I documented my experiments here.
Update on Jan, 5 2011: Corrected code typos and added a live demo page. Corrected some curly braces that were pointing the wrong way, and updated vendor prefix property references, so -webkit and -moz are listed before the non-prefixed property.
First, here is the amazing button that we want to create:

Its active state will be triggered on either mouseDown or a touch event (for iOS and Android devices, say). In that case, we want the colors to change as shown:

Of course, ideally we could create this button using pure CSS styles, like border-radius and box-shadow, or with SVG vector art. But let’s say that for whatever reason, this button has visual elements that can’t be created 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 property lets us specify an image to stretch to fit our button, no matter how wide or tall it becomes. Usually, border-image is used with an image like this:
![]()
This image is 40 pixels 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 pixels from either side of the image and stretch it to fit. Of course, we’d then have to overlay the button text on top of this background. (Be sure to specify the vendor-specific prefixed properties first. For clarity, I’ll omit the vendor prefixes below.)
The old-fashioned way to create the button’s active state is to generate a second PNG:
![]()
…and style it with more CSS. Note that we’ve added the .pressed class, and we’re now referencing a different 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 #buttonElement whenever it is clicked or tapped.
But by using the CSS sprites technique, we can include both button states in a single image. (This one is 40 pixels by 100.)
![]()
Then, we modify the border-image parameters to reference different parts of our sprite image, depending on the button 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” values? They are the key to shifting the border-image reference up and down, as needed.
Now isn’t that better? Just one image means fewer network requests, plus the button’s active state is already preloaded into memory — no need for extra weird JavaScript!
