My last post was about using SVG values for the background-image
property, and I pointed out one big problem with the technique:
The drawback of this is that it’s not ready for use just yet—browsers that don’t support SVG in
background-image
will not provide any fallback, even if you supply anotherbackground-image
value; so in non-supporting browsers, no image at all will be displayed.
This was annoying me a little, and I couldn’t find any workarounds that didn’t use JavaScript. However, after a bit of head-scratching I’ve come up with a way to get around it.
In order to have browsers which don’t support SVG display 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 support SVG and multiple backgrounds will show the SVG; browsers which don’t support SVG will fall back to the PNG in the background-image
stack. As far as I know, all browsers which support SVG also support multiple backgrounds; any which don’t (i.e. IE) will fall back to the PNG in the first background-image
declaration.
Update: Thanks to a comment I have amended this technique. IE was not falling back to the first background-image
as it should have done, but I got around this by adding a none value in the declaration. This invalidates the rule in IE and forces the fallback to the previous declaration.
You can see a demonstration of it here: Background SVG with PNG fallback – demo.
This technique is not without its own caveats, however. First and foremost, the SVG must not have a transparent background, or else the PNG in the background layer below it will probably show through. Also, depending on how fast the SVG file loads, the PNG file may show up first and then be covered over.
Finally, if you’re using the same background image at different sizes and either your browser doesn’t support background-size
or you don’t want automatically 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 creating multiple PNG images anyway, 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 completely bulletproof technique, but I offer it in the hope that in certain circumstances it could be useful.
Why can’t you do this:
E {
background-image: url(‘image.png’);
background-image: url(‘image.svg’), url(‘image_that_doesnt_exist.png’);
background-size: 100% 100%;
}
Martin [June 15th, 2010, 19:35]
It’s a good idea, Martin, but it doesn’t work. The fallback to the previous
background-image
declaration would only happen if the browser didn’t understand the multiple syntax. However, some browsers – notably Firefox – do understand the syntax, so the fallback doesn’t occur, but can’t locate the file for thebackground-image
, so treat it as none.Peter [June 15th, 2010, 22:29]
Just checked this on Windows 7, Internet Explorer 8… all squares blank. And the background on Firefox 3.6.6 does not scale to browser width, so the Safari 5.x and Chrome 5.x works. Guess this trick is obsolete now.
BerggreekDK [July 1st, 2010, 18:21]
Hmm, it worked in IE8 when I last checked; I’ll take another look later and confirm. Thanks for the feedback.
Update: You were right; IE was not falling back as expected. I’ve updated the technique so it works as expected now. Thanks a lot for spotting this.
Peter [July 1st, 2010, 19:33]
The drawback of this method is obviously that if the browser supports the CSS 3
background-image
property, the browser will fetch both images and lay them over each other (the SVG will be on the first layer and should cover the PNG, so that it becomes invisible). This also means that without optimisation (that is browser detects that the SVG covers the entire area and ignores all other layers) the browser will have to scale both images.Matthias [August 9th, 2010, 00:29]
You’re right, Matthias, it’s certainly not a method without any drawbacks; I’d hesitate to use it on a production site because it has so many limitations.
Peter [August 9th, 2010, 10:11]
Chrome breaks it. It causes both the SVG and the PNG to display simultaneously.
Bryan Elliott [September 21st, 2010, 19:51]
I don’t quite get what you mean, Bryan; it’s showing the two side-by-side? Or one appears under the other? I’m looking at it in Chrome now and it seems fine.
Peter [September 21st, 2010, 22:41]
Hey Peter, like the idea, why not just make the second background image a 1x1px transparent 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.
Henry [May 23rd, 2012, 06:20]
This solution does not work on Android 2.3 :(
Somebody know a solution for that?
Extranion [August 22nd, 2012, 10:23]
Hello Peter. Just wanted to say thanks for posting this method. With the rise of high resolution screens like Apple’s retina displays, I was looking into using SVG with some sort of image fallback. This CSS method did exactly what I needed to do :)
Wil Alambre [September 6th, 2012, 17:05]
I created a similar technique but using CSS gradient to discriminate SVG support. Instead of none I use an invisible gradient. That solves the problem with Anddroid 2.X browsers. More details at http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique
Pau [November 26th, 2012, 23:24]
Use this instead:
img {
background: #ffffff url(image.png);
background: rgba(255,255,255, 1.0) url(image.svg);
}
Note the ‘rgba’ instead of ‘none’.
This way you will get a svg+white background with rgba and since IE<=8 does not support rgba a fallback to png+white background the old way.
Infinum [February 2nd, 2013, 18:36]
rgba works IE7 (use png), FF, Chrome
Bandzula [February 25th, 2013, 19:12]
If you use conditionals on the html tag you can do the following which eliminates any worries about multiple backgrounds or load times:
E { background: url(img/header-sprite.svg);}
.ie8 E { background: url(img/header-sprite.png);}
http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/
Larry Botha [April 19th, 2013, 14:26]
Thanks, it worked!
Melissa [August 28th, 2013, 12:58]