Using SVG in backgrounds with PNG fallback

Warning This article was written over six months ago, and may contain outdated information.

My last post was about using SVG val­ues for the background-image prop­er­ty, and I point­ed out one big prob­lem with the technique:

The draw­back of this is that it’s not ready for use just yet—browsers that don’t sup­port SVG in background-image will not pro­vide any fall­back, even if you sup­ply anoth­er background-image val­ue; so in non-sup­port­ing browsers, no image at all will be displayed. 

This was annoy­ing me a lit­tle, and I could­n’t find any workarounds that did­n’t use JavaScript. How­ev­er, after a bit of head-scratch­ing I’ve come up with a way to get around it.

In order to have browsers which don’t sup­port SVG dis­play a PNG image instead, you should use this code:

E {
background-image:  url('image.png');
background-image:  none,url('image.svg'), url('image.png');
background-size: 100% 100%;
}

Browsers which sup­port SVG and mul­ti­ple back­grounds will show the SVG; browsers which don’t sup­port SVG will fall back to the PNG in the background-image stack. As far as I know, all browsers which sup­port SVG also sup­port mul­ti­ple back­grounds; any which don’t (i.e. IE) will fall back to the PNG in the first background-image declaration.

Update: Thanks to a com­ment I have amend­ed this tech­nique. IE was not falling back to the first background-image as it should have done, but I got around this by adding a none val­ue in the dec­la­ra­tion. This inval­i­dates the rule in IE and forces the fall­back to the pre­vi­ous declaration.

You can see a demon­stra­tion of it here: Back­ground SVG with PNG fall­back — demo.

This tech­nique is not with­out its own caveats, how­ev­er. First and fore­most, the SVG must not have a trans­par­ent back­ground, or else the PNG in the back­ground lay­er below it will prob­a­bly show through. Also, depend­ing on how fast the SVG file loads, the PNG file may show up first and then be cov­ered over.

Final­ly, if you’re using the same back­ground image at dif­fer­ent sizes and either your brows­er does­n’t sup­port background-size or you don’t want auto­mat­i­cal­ly resized PNGs, you’ll have to use the rule for each instance:

E {
background-image:  url('image-small.png');
background-image:  none,url('image.svg'), url('image-small.png');
background-size: 100% 100%;
}

F {
background-image:  url('image-large.png');
background-image:  none,url('image.svg'), url('image-large.png');
background-size: 100% 100%;
}

And of course, if you’re cre­at­ing mul­ti­ple PNG images any­way, you may feel there’s not a lot of point in using SVG images as well. I’ll be the first to admit that this isn’t a com­plete­ly bul­let­proof tech­nique, but I offer it in the hope that in cer­tain cir­cum­stances it could be useful.

16 comments on
“Using SVG in backgrounds with PNG fallback”

  1. Why can’t you do this:

    E {
    back­ground-image: url(‘image.png’);
    back­ground-image: url(‘image.svg’), url(‘image_that_doesnt_exist.png’);
    back­ground-size: 100% 100%;
    }

  2. It’s a good idea, Mar­tin, but it does­n’t work. The fall­back to the pre­vi­ous background-image dec­la­ra­tion would only hap­pen if the brows­er did­n’t under­stand the mul­ti­ple syn­tax. How­ev­er, some browsers — notably Fire­fox — do under­stand the syn­tax, so the fall­back does­n’t occur, but can’t locate the file for the background-image, so treat it as none.

  3. Just checked this on Win­dows 7, Inter­net Explor­er 8… all squares blank. And the back­ground on Fire­fox 3.6.6 does not scale to brows­er width, so the Safari 5.x and Chrome 5.x works. Guess this trick is obso­lete now.

    BerggreekDK [July 1st, 2010, 18:21]

  4. Hmm, it worked in IE8 when I last checked; I’ll take anoth­er look lat­er and con­firm. Thanks for the feedback.

    Update: You were right; IE was not falling back as expect­ed. I’ve updat­ed the tech­nique so it works as expect­ed now. Thanks a lot for spot­ting this.

  5. The draw­back of this method is obvi­ous­ly that if the brows­er sup­ports the CSS 3 background-image prop­er­ty, the brows­er will fetch both images and lay them over each oth­er (the SVG will be on the first lay­er and should cov­er the PNG, so that it becomes invis­i­ble). This also means that with­out opti­mi­sa­tion (that is brows­er detects that the SVG cov­ers the entire area and ignores all oth­er lay­ers) the brows­er will have to scale both images.

  6. You’re right, Matthias, it’s cer­tain­ly not a method with­out any draw­backs; I’d hes­i­tate to use it on a pro­duc­tion site because it has so many limitations.

  7. Chrome breaks it. It caus­es both the SVG and the PNG to dis­play simultaneously.

    Bryan Elliott [September 21st, 2010, 19:51]

  8. I don’t quite get what you mean, Bryan; it’s show­ing the two side-by-side? Or one appears under the oth­er? I’m look­ing at it in Chrome now and it seems fine.

  9. Hey Peter, like the idea, why not just make the sec­ond back­ground image a 1x1px trans­par­ent png, seem to work fine for me. Also i base 64’ed the png in as its such a tiny file its now worth the extra request.

  10. This solu­tion does not work on Android 2.3 :(

    Some­body know a solu­tion for that?

  11. Hel­lo Peter. Just want­ed to say thanks for post­ing this method. With the rise of high res­o­lu­tion screens like Apple’s reti­na dis­plays, I was look­ing into using SVG with some sort of image fall­back. This CSS method did exact­ly what I need­ed to do :)

  12. I cre­at­ed a sim­i­lar tech­nique but using CSS gra­di­ent to dis­crim­i­nate SVG sup­port. Instead of none I use an invis­i­ble gra­di­ent. That solves the prob­lem with And­droid 2.X browsers. More details at http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique

  13. Use this instead:
    img {
    back­ground: #ffffff url(image.png);
    back­ground: rgba(255,255,255, 1.0) url(image.svg);
    }

    Note the ‘rgba’ instead of ‘none’.
    This way you will get a svg+white back­ground with rgba and since IE<=8 does not sup­port rgba a fall­back to png+white back­ground the old way.

  14. rgba works IE7 (use png), FF, Chrome

  15. If you use con­di­tion­als on the html tag you can do the fol­low­ing which elim­i­nates any wor­ries about mul­ti­ple back­grounds or load times:

    E { back­ground: url(img/header-sprite.svg);}
    .ie8 E { back­ground: url(img/header-sprite.png);}

    http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/

  16. Thanks, it worked!