CSS performance: UI with fewer images

December 23rd, 2009. Tagged: CSS, images, performance

2010 update:
Lo, the Web Performance Advent Calendar hath moved

Dec 23 This post is the one-before-last article in the 2009 performance advent calendar experiment.

Often performance improvements come with their drawbacks, sometimes improving performance causes pains in other parts of the development process or strips stuff from the final product. Sometimes there's even a conflict where you have to pick: slow, unusable and beautiful or fast and looking like hacked with a blunt axe. But it doesn't have to be this way.

This post outlines some approaches to achieving common UI elements using CSS tricks in as many browsers as possible while using as fewer images as possible. Some of the tricks are brand new, some are very, very old, IE5.5. old. They all have in common the "fewer or no images" mantra. Using fewer images comes with some pronounced benefits:

  • less time spent in Photoshop
  • lighter page, less HTTP requests, less image payload
  • fewer elements in the sprite to maintain (and sometimes fewer sprites) which means longer lived sprites with fewer updates and cache invalidation
  • generally easier maintenance - it's much easier to change a color value than to update and push a new image version

Sometimes some browsers may not be fully supported but that's ok - as long as there's progressive enhancement and the basic page is usable, people rarely notice 1px glows and other ornaments.

So let's get started. BTW, a test page with the stuff discussed in the post is here.

Rounded corners

Yep, let's tackle the biggie.

Forget rounded corners in browser that don't support border-radius. Period. It may be hard to argue this case, but definitely try. Doing rounded corners any other way than border-radius is a pain - it adds markup bloat, it makes you create more images or sprite elements. It's tougher to update. Just forget it. Forget rounded corners in IE < 9 (as rumor has it border-radius is coming to IE9). People may argue that IE is important for your audience. No doubt that's true, but rounded corners are not so important for the audience. Show your designer Yahoo Search results page - the sidebar on the left-hand side. Not very rounded in IE. Do you think this was an easy battle - losing rounded corners in IE for such a high-profile site? Ask the man who won the battle ;)

So starting with a normal module - head, body and border:

The markup - nice and clean:

  <div class="module">
    <div class="hd"><h3>This is the header</h3></div>
    <div class="bd">
      <p>Here comes the content</p>
      <p>Here comes some more</p>
      <p>You can never have too much content, because
         content is king, right?

Some fairly simple border radius to support Firefox, Webkit (Safari, Chrome, iPhone...) and, since a few days ago, Opera 10.5 alpha:

.module {
  -moz-border-radius: 9px; 
  -webkit-border-radius: 9px; 
  border-radius: 9px;


This is it! Easy-peasy, lemon squeezy.

Now, it's a little annoying to write three declarations for the same thing, but, hey - beats images and extra markup hands down. Also annoying are the differences when setting individual corners (-moz-border-radius-topleft is -webkit-border-top-left-radius). In this case we need to also round the header (class .hd) so it doesn't bleed through the beautifully rounded corners:

.hd {
  -moz-border-radius: 8px 8px 0 0; 
  -webkit-border-top-left-radius: 8px; 
  -webkit-border-top-right-radius: 8px; 
  border-radius: 8px 8px 0 0;


  • Full support: Firefox, Safari, Chrome, Opera 10.5
  • Fallbacks: IE (corners are not rounded)

Drop shadows and glows

Another favorite effect designers love - dropping shadows. It's easy to enhance that existing .module without any new images:

.module {
  /* offset left, top, thickness, color with alpha */
  -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); 
  -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); 
  box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
  /* IE */
  filter:progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray');
  /* slightly different syntax for IE8 */
  -ms-filter:"progid:DXImageTransform.Microsoft.dropshadow(OffX=5, OffY=5, Color='gray')";

And now our module casts a shadow:

Now two notes for IE: first the shadow doesn't have alpha so it's not as nice and second, this filter may not play along with other filters in the same module. But the shadow is cast and that's a check for IE too, even IE5.5!

You may notice that in this case we basically need to more or less repeat the same declaration three times and the IE declaration two times. This is irksome, but hopefully keeping the strings close together should help gzip compression.

As for glowing, it's the same thing in FF, Webkit, Opera, only without any offset. For IE, there's a different filter called glow:

.glow {
  -webkit-box-shadow: 0 0 10px rgba(50, 50, 50, 0.8);
  -moz-box-shadow: 0 0 10px rgba(50, 50, 50, 0.8); 
  box-shadow: 0 0 10px rgba(50, 50, 50, 0.5);
  filter:progid:DXImageTransform.Microsoft.glow(Strength=5, Color='gray');
  -ms-filter:"progid:DXImageTransform.Microsoft.glow(Strength=3, Color='gray')";

I added these declaration to a new class .glow so I can add the class name to modules that need to glow. The result:

The result as it glows in IE:

Now you see why I added only 3 pixels glow in IE and whole 5 in the rest. The IE glow is a little .. interesting. Also in IE8 (could be my VM, in IE6 XP no VM all looks OK) the glow seems to move slightly when you hover over the module.

Verdict for shadows and glows:

  • Full support: FF, Safari, Chrome, Opera, IE5.5 and up

More info:


Ah, gradients. Sometimes so subtle that we, muggles and other mere mortals, don't see them even when we try our hardest. But for the designer they could be life/death situation.

Let's make the head (class .hd) of our module a gradient without any images:

.hd {background-image: -moz-linear-gradient(top, #641d1c, #f00); 
  background-image: -webkit-gradient(linear, left top, left bottom, from(#641d1c), to(#f00));
  -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff641d1c,endColorstr=#ffff0000)";

The result:

What a beautiful (code-speaking, of course, not so sure about visually beautiful) module. It has rounded corners, drop shadows and a gradient and so far we haven't used even a single image. Which means this reddish module can become blue, green or, god forbid, pink - with a single tweak in the code, the CMS or the user preferences (if you're building a social network for example).

Gradients verdict:

  • Full support: FF, Safari, Chrome, IE
  • Fallbacks: Opera (solid color)

More info:

... and RGBA for all

Being able to set the transparency of the background without affecting the transparency of the foreground (the text) is quite handy. That's why there's rgba() in CSS (red, green, blue, alpha). IE is not yet supporting it, but we can use the gradient filter which does support transparency. In this case we don't need the actual gradient so we set start and end color to the same thing. Also the background: transparent is needed for the whole thing to work in IE:

.rgba {
  background-color: transparent;
  background-color: rgba(200,200,200,0.8);
  -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99dddddd,endColorstr=#99dddddd)";

The result is pleasantly cross-browser:

RGBA verdict

  • Full support: Firefox, Safari, Opera, Chrome, IE

Rotating images

It happens that sometimes you use the same image only flipped. For example open/close thingies, menus and such. How about reusing the same image and rotating it with CSS?

.arrow {background: url(arrow.png) no-repeat; display: block; float: left; width: 33px; height: 33px;}
.right{ /* this is the original image*/ }
.left {
  -moz-transform: rotate(180deg);-webkit-transform: rotate(180deg); -o-transform: rotate(180deg); 
  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
.up {
  -moz-transform: rotate(270deg);-webkit-transform: rotate(270deg); -o-transform: rotate(270deg); 
  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
.down {
  -moz-transform: rotate(90deg);-webkit-transform: rotate(90deg); -o-transform: rotate(90deg); 
  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);

Here's the result. Single image:



the HTML:

<span class="arrow right"></span>
<span class="arrow left"></span>
<span class="arrow up"></span>
<span class="arrow down"></span>

You may notice that the CSS could be quite verbose for saving such small images. It's highly recommended you add the rotation code to a class and use the class name when necessary instead of repeating the same long declaration for every use case or image. Then pray to the gods of compression that this thing gzips well ;)


  • Full support: Firefox, IE, Safari, Opera, Chrome

Multiple UI elements with the same background image

The last few tricks have something in common - they each use one background image. The background images are very small - usually around 100 bytes. The tiny image has some transparency to it and is placed as a background-image which sits on top of a background-color. Because of the transparency, the background color shines through, but differently depending on the transparency level of the image above it.

The result is - different UI elements with different colors (and even different hover colors) which can be part of CMS or part of user's skinning and they all reuse the same tiny background. So what can we do this way? A lot of interesting background effects, but here's a few.

Glossy buttons

Here's the end result:

All these buttons share the same background image. The image is 1x1000 and repeated horizontally. The 1000 is just to be safe, very safe, because 50, 100 or 1000 doesn't affect the file size which just a mere 100 bytes. The upper half of the image is a little less transparent. The lower half is 100% transparent. When placed on top of the solid color the whole thing looks shiny and glossy. And you can change the color any way you like.


<p class="button">button1<p>
<p class="button button2">button2<p>
<p class="button button3">button3<p>
<p class="button button4">button4<p>
<p class="button button5">button5<p>
<p class="button button6">button6<p>

And the CSS can't be simpler:

.button {
  background-position: center;
.button:hover {background-color: #F29222;}
.button2 {background-color: #A41D1C;}
.button3 {background-color: #0F6406;}
.button4 {background-color: #333f79;}
.button5 {background-color: black;}
.button6 {background-color: orange; color: black;}

Actually in the test page I have inlined the image with data URI to save the whole HTTP request for such a teeny image.

As you can see in the URL of the background, I've done a little script to generate some background images:
The image generator's source code is right here.


Same technique - but used to generate striped background:

It's basically the same code, only using a different call to the image generator to give us a different background image.


  <div class="module stripe earth glow">
    <div class="bd">
      <p>striped background</p>
  <div class="module stripe tech glow">
    <div class="hd phony stripe"><h3>stripity-stripes</h3></div>
    <div class="bd">
      <p>striped background with the same background image</p>


.stripe {background-image: url(http://tools.w3clubs.com/mask/mask.php?type=stripe);}
.earth  {background-color: olive;}
.tech   {background-color: #bbb;}
.phony  {background-color: #0F6406;}

Again, this image can be a data URI so we save the single HTTP request.

And another gradient

So if you don't like the previously discussed way to do gradients, here's another one. The same trick with the solid color background and a semi-transparent image on top.


The background images as generated by the service are:
// lighter at the top
// darker at the top

Again, you can see the test page here and the source for the image generation is here.

For yet another example of this technique check my post on this (abandoned) blog phonydev.com. There I take an image and a mask image generated by the same script and overlay to achieve an iPhone-like glossy button.

iphone glossiness


Kind of long post, but I hope you're excited about removing a bunch of images from your future designs. If I've omitted some details, please let me know in the comments.

Tell your friends about this post: Facebook, Twitter, Google+

48 Responses

  1. Will those semi-transparent PNG techniques (the glossy button stuff) work in IE6?

  2. Awesome article!

    Thanks for the great work putting this together for all of us.

    Happy 2010!!

  3. Intersting tricks, but IE’s filters have a performance penalty, and a real weight on memory.
    I support the rational for dropping rounded corners in IE, but the same reasing applies to drop-shadows and other fancy stuff.

    In the end, it would be worth to compare the performance of your proposed solutions with traditional photoshop-based solutions.

  4. Great article indeed! Thanks

  5. How about paint performance and CSS? How does that compare against images?

  6. Actually, there’s some performance impact on using CSS instead of images. I made some research a year ago, studying how HTML/CSS affects performance (JS-animation mostly). For example, semi-transparent image works faster than opaque image + CSS opacity. There’s also a big difference on using images as background-image CSS property or as <img> in Firefox (<img> is much faster).

  7. good bit of research here. I’ll bookmark this as a reference page.

  8. Great article, congratulations, Stoyan!

    I also would like to know the answer of arnout’s question.

  9. Great summary. This is the post that’s been missing for a while.

    Hedgerwow had an article on Fuzzy Imageless Shadows a bit ago (offline now?) that in IE used a black background along with the Blur filter. Thus it had alpha. ‘Twas verrry nice.

  10. Continuing last comment.. Here’s the cached demo page: http://bit.ly/8S2fAK

    and the CSS looks something like:

    /* supported: ff3.5+,Saf3+, Chrome, IE6+ */
    /* not supported: opera (also 10.5) */
    .shadow {
    box-shadow: 3px 3px 14px rgba(100,100,100,.7);
    -moz-box-shadow: 3px 3px 14px rgba(100,100,100,.7);
    -webkit-box-shadow: 3px 3px 14px rgba(100,100,100,.7);
    background: #000\9; /* hack to isolate IE */
    filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=14,makeshadow=’true’, shadowopacity=0.7);
    -ms-filter:progid:DXImageTransform.Microsoft.Blur(pixelradius=14,makeshadow=’true’, shadowopacity=0.7);

  11. Great!

  12. I found very useful this tutorial. I wonder when will Microsoft will simplify the syntax? What does all progID bla bla syntax is useful for..?

  13. Wow! Superb article!
    Thank you for the great work.

  14. don’t forget sprites to limit the number of hits to the server if ever you need lots of pictos

  15. Cool bag of tricks there. I knew a few but stuff like gradients and transparency was new to me.

    Good job overall.

  16. Maybe it is just me, but the drop shadows don’t work on a div in IE (but will applied to a table).
    Am I doing something wrong?

  17. Nice!

    Just a small thing, perhaps the buttons should have the css for the hand cursor.

  18. Thanks for this wrap up Stoyan. It inspired me to revisit the MS filters and reminded me of the “light” filter that solved a CSS problem I had.

    Also, if you combine the “glow” filter with a clipping wrapper element then you can achieve a drop shadow in IE6 css.


    Thanks for the inspiration!

  19. Great tutorial & example. Save my time to build web site so much.

    Thanks for your work.

  20. [...] and webkit based browsers (such as Safari and Chrome) requires slightly different CSS code. See CSS Performance with Fewer Images for a full listing of all the available CSS3 styles to replace [...]

  21. putting instruction Im putting my mark here. Get it? Putting?
    Can I have a laugh now? No?

  22. [...] and webkit based browsers (such as Safari and Chrome) requires slightly different CSS code. See CSS Performance with Fewer Images for a full listing of all the available CSS3 styles to replace [...]

  23. [...] (source) [...]

  24. [...] (source) [...]

  25. [...] (source) [...]

  26. [...] 译自:CSS performance: UI with fewer images 原作者:Stoyan Stefanov 请尊重版权,转载请注明来源,多谢! [...]

  27. [...] backgrounds are a common visual element on modern websites. And while you can use browser specific CSS extensions to directly create gradient backgrounds with pure CSS the much more common method is to use an [...]

  28. [...] Posted by 纷飞De萝卜|罗红胜 on 六 16, 2011 in HTML&CSS | 0 comments译自:CSS performance: UI with fewer images 原作者:Stoyan Stefanov [...]

  29. [...] dataURIs instead of MS filters because of this.  If you want more examples, Stoyan Stefanov has a great post that goes into a lot of detail about writing high performance, CSS driven UI’s using [...]

  30. where to buy Premier and Stitched Los Angeles Lakers jerseys ??…

    [...]we adivce go to this website to see more about Los Angeles Lakers jerseys and others. [...]…

  31. [...] на нови селектори като box-shadow не е невъзможно, тъй като те имат поддръжка [...]

  32. scottsdale teeth whitening…

    [...]CSS performance: UI with fewer images / Stoyan’s phpied.com[...]…

  33. [...] 译自:CSS performance: UI with fewer images 原作者:Stoyan Stefanov 请尊重版权,转载请注明来源,多谢! [...]

  34. Twitter Expert in London…

    [...]CSS performance: UI with fewer images / Stoyan’s phpied.com[...]…

  35. [...] CSS3 for Better Performance [...]

  36. radical!…

    this was amazing….

  37. [...] CSS3 for Better Performance [...]

  38. U rocking …tahnk alot for compatability browser support… like gradient,glow,roundcorners

  39. [...] 译自:CSS performance: UI with fewer images 原作者:Stoyan Stefanov 请尊重版权,转载请注明来源,多谢! [...]

  40. I am regular visitor, how are you everybody? This piece of writing
    posted at this web page is truly nice.

  41. I have been surfing online greater than three hours nowadays, but I never discovered any fascinating article like yours.
    It is lovely price enough for me. Personally, if all site owners and bloggers made
    excellent content material as you did, the net will be much more helpful than ever before.

  42. Altagracia Strowe

    This may not be the best place to ask but, I have been searching for a place to take my Mac for repair. Has anybody tried this apple repair service? They’re located in West Los Angeles, which is only 5 minutes from my office. It’s called – Mac Repair Los Angeles, 11322 Santa Monica Blvd, Ste B Los Angeles, CA 90025 (310) 966-9099.

  43. Someone essentially help to make critically articles I would state. This is the first time I frequented your web page and thus far? I surprised with the research you made to make this particular publish amazing. Magnificent process!

  44. […] 있습니다. 이러한 효과들을 이용하여 재미있는 효과들을 줄 수 있습니다. CSS3를 이용하지 못하는 구식 브라우저 사용자들을 […]

  45. Spot on with this write-up, I honestly think this amazing site
    needs far more attention. I’ll probably be back again to see more, thanks for the info!

  46. I think this is among the most important information for me.
    And i am glad reading your article. But want to remark on few
    general things, The site style is great, the articles is really excellent :
    D. Good job, cheers

  47. Good day! Do you use Twitter? I’d like to follow you if that would be
    okay. I’m definitely enjoying your blog and look forward to new updates.

  48. This design is incredible! You definitely know how to keep a reader amused.
    Between your wit and your videos, I was almost
    moved to start my own blog (well, almost…HaHa!) Excellent
    job. I really enjoyed what you had to say, and more than
    that, how you presented it. Too cool!

Leave a Reply