Here at CSS-Tricks, there’s a lot of information telling you how wonderful SVG is. And as much as we want to convince you that SVG is for Everybody, SVG isn’t as widely used as we would like. In fact, some people still (literally) don’t get SVG.
Maybe they are stuck on an office computer that hasn’t been updated since the IT guy quit six years ago, maybe they’re using a second-hand refurbished phone they can’t afford to upgrade, or maybe they just don’t understand or don’t care about updating to a new browser. Whatever the reason, approximately 5% of users browsing the web are doing so with web browsers that can’t display SVG — more in the USA (according to caniuse data).
Some tech-oriented web sites (like this one) can afford to say that they only support modern web browsers. But for most sites, you can’t ignore 1 in 20 potential customers. If you want to give the 95% of users on modern browsers all the benefits of SVG, but still provide a functional experience for the others, you need a fallback plan.
The guide is written by Amelia Bellamy-Royds and me, Chris Coyier. Amelia and I presented at the same conference together. We both covered SVG, yet neither of us SVG fallbacks comprehensively. It’s such a huge topic, after all. While I’ve covered SVG fallbacks before, it’s been a few years and we figured we could do that subject better justice now. Here we go!
What KIND of fallback do you need?
Before getting into the technical options for implementing fallbacks, it helps to stop and think of what kind of fallback you need:
- No fallback. If the SVG is an icon whose meaning is clearly expressed by a text label, you may be able to let that icon disappear without compromising the site’s functionality.
- Text fallback. If the SVG is an icon whose meaning could be expressed by a text label, maybe all you need is to ensure that alt text shows up in its place.
- Image fallback. This is what most people think of as an SVG fallback: a PNG or GIF image that represents the same graphic, just with a larger file size and poorer resolution.
- Interactive fallback. For replacing animated and interactive SVG, a PNG may not do justice. You’ll need a graphics language with an interactive DOM.
On the interactive front, your options are limited. You could convert the SVG to Flash and then rewrite all your interaction code in ActionScript. Or you could use Raphaël to both draw and manipulate the graphic. It provides a single JavaScript interface to both SVG and the VML vector graphics supported in Internet Explorer 6-8. Which means that Raphaël doesn’t help with old mobile browsers, but it does cut down the number of unsupported users significantly.
The rest of this post covers the ways to create text and image fallbacks for your SVG. Your options will depend almost entirely on how you’re including that SVG in the web page in the first place: as an embedded object, as inline SVG code, as an image in the HTML, or as an image in the CSS.
<img>
Fallback for SVG as
<img>
SVG can be used like this:
<img src="image.svg" alt="">
Here are some options for browsers that don’t support that.
How to Test for Support
Even though this test is supposed to test for the <image>
element within SVG, thanks to some testing we’ve proven it works for SVG used as the source for <img>
elements as well. It’s this easy:
function svgasimg() {
return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
}
Source Swapping
The test above is what the SVGeezy library uses to do SVG-as-img fallbacks. If the browser fails this test, it will swap out the SVG with a PNG as needed. As in <img src="image.svg">
turns to <img src="image.png">
. You create the PNG versions yourself and have them available in the same directory.
- Advantages: It’s easy. It works. You can even write your own source-swapping JavaScript pretty easily.
- Disadvantages: It’s likely the non-supporting browsers will download two images, which is a performance penalty. It will download the SVG version (at least enough to know it can’t use it) and then the PNG version.
Here’s a very simple no-dependency example of source swapping:
Swapping in a fallback image, in Internet Explorer 6(!)
If for some reason you couldn’t get this into external JavaScript and had to do it inline…
<img src="image.svg" onerror="this.src='image.png'; this.onerror=null;">
If you were doing feature detects with Modernizr and also utilizing jQuery on a project, it could be as simple as this:
if (!Modernizr.svg) {
$("img[src$='.svg']")
.attr("src", fallback);
}
SVGInjector is a JavaScript library that can help with <img>
fallbacks in specific scenarios. It’s main purpose is to replace <img>
with inline SVG.
<img class="inject-me" src="image-one.svg">
var mySVGsToInject = document.querySelectorAll('img.inject-me');
var injectorOptions = {
pngFallback: 'assets/png'
};
SVGInjector(mySVGsToInject, injectorOptions);
SVGMagic is another JavaScript library that swaps out sources with PNG versions, including SVG used in <img>
, background-image, or even inline. It’s biggest advantage is that it creates the PNG version for you automatically, by request to a third-party server. So just beware the dependencies (jQuery and third-parties) and test for speed and reliability.
The <picture>
Element
The <picture>
element allows for fallback images when the browser doesn’t support a specified image format.
<picture>
<source type="image/svg+xml" srcset="image.svg">
<img src="image.png" alt="">
</picture>
Unfortunately that’s not much use when support for SVG is much better than support for <picture>
.
You can polyfill the picture element though, with Picturefill. Sara Soueidan has an article that covers this.
- Advantages: Not only can you get fallback images, you can specify different fallbacks at different media queries, as part of the picture syntax. Meaning differently sized fallbacks for different screens, art direction, etc.
- Disadvantages: Polyfill needed. To avoid double-downloads, you need to skip the src on the
<img>
within<picture>
, which is invalid and means you’ll need the polyfill forever.
The <image>
Trick
With some inline SVG trickery we can pull this off:
<svg width="96" height="96">
<image xlink:href="image.svg" src="image.png" width="96" height="96" />
</svg>
Be sure to add sizing attributes to the image so that it fills up the entire SVG, and then size both svg
(for modern browsers) and image
(for old ones) in your CSS.
Besides excluding a few older browsers that can support SVG images but not inline SVG, the main limitation here is that it will be a lot more work to get the images to display the correct size on a fluid site. You’ll need to use the same tricks used to get an SVG to scale to a consistent aspect ratio, instead of just setting width: 100%;
and letting the height adjust automatically.
- Advantages: Requires no JavaScript or any other dependencies.
- Disadvantages: Triggers multiple (aborted) requests in IE. Older iOS can display fallback even though SVG is supported. Tests.
<object>
Fallback for SVG as
<object>
As inline SVG has become better supported, SVG as <object>
has fallen out of favor. That’s unfortunate from a progressive enhancement/graceful degradation perspective, because this is the easiest way to provide fallback content.
The child content of the <object>
element is displayed if the object itself cannot be. That content can be any html content: images, formatted text, even another object (for example, one containing a Flash version of your animated SVG).
<object type="image/svg+xml" data="svg-ok.svg">
<img src="svg-no.png" alt="No SVG support">
</object>
<object type="image/svg+xml" data="svg-ok.svg">
<p class="warning">
Your browser does not support SVG!
</p>
</object>
Another progressive-enhancement benefit of <object>
: some users on Internet Explorer 8 (and under) will be able to see the SVG! Objects trigger plug-ins, and the no-longer-updated Adobe SVG Viewer plug-in is still available for download for old IE.
background-image
Fallback for SVG as CSS
background-image
For most CSS properties, you can trust in CSS error handling to get the browser to ignore a new syntax and apply a value declared earlier in the cascade. However, the syntax of a CSS rule using an SVG image file is perfectly correct for the old browsers. So they apply the rule, download the file, but then don’t know what to do with it.
The trick here is to find syntax that is supported by (nearly all) browsers that support SVG, but not by the older browsers. Here’s the magic:
body {
background: url(fallback.png);
background: url(background.svg),
linear-gradient(transparent, transparent);
}
This combines two features that, together, target the perfect combo. If a browser supports both multiple backgrounds and linear gradients, it supports SVG too. So here we declare the SVG with a completely transparent linear-gradient. If that fails in an older browser, the fallback declared above will kick in.
You might add a slight performance hit on modern browsers from having to calculate the transparent gradient, but it’s probably negligible.
<svg>
Fallback for inline
<svg>
Inline SVG is popular for many reasons. All the code to draw what needs to be drawn is either right there in the markup (reducing requests), or referenced from a file that can be cached. You have lots of control over SVG that is inline. It’s in the DOM, so it can be controlled with CSS and JavaScript from the same document.
How to test for support
There are some great tests from Modernizr that we can look at. Your best bet for inline SVG, extracted out into a function, is this:
function supportsSvg() {
var div = document.createElement('div');
div.innerHTML = '<svg/>';
return (div.firstChild && div.firstChild.namespaceURI) == 'http://www.w3.org/2000/svg';
};
This tests if the HTML parser (which is what is used to parse content sent to innerHTML) can correctly generate an SVG element, basically by creating an element, injecting SVG, and testing the namespace.
As with <object>
, a browser that doesn’t recognize <svg>
will just ignore your SVG markup and treat its contents as HTML. But its not that simple. With <object>
, browsers that do support the object will know to ignore the child content. With inline SVG, there’s no such built-in fallback. However, there are a few workarounds you can use.
Plain text fallback inside inline SVG
You can include plain text inside your inline SVG code, and it will be ignored by any browser that supports SVG, because SVG text must be contained in a <text>
element. You can even include hyperlinks within that text.
<svg viewBox="-20 -20 40 40">
<!--Text Fallback-->
I'm sorry, your browser does not support
<circle fill="limegreen" r="19" />
<path stroke="forestgreen" fill="none" stroke-width="6"
d="M-12,3 L-3,10 11,-12" />
<text dy="0.35em" text-anchor="middle" font-weight="bold"
font-size="18px" font-family="sans-serif"
fill="indigo">SVG</text>
<!--Fallback with links-->
Please upgrade to a <a href="http://browsehappy.com/?locale=en">modern browser</a>.
</svg>
Things to note:
- The SVG text (the word “SVG” in this demo) becomes part of the fallback text. We’ll talk about how to avoid this in a moment.
- The SVG
<title>
text does not become part of the fallback text. That’s because the no-SVG browsers interpret this as an invalid second HTML title element, and ignore it.
You cannot include any other HTML content within inline SVG: when the HTML parser in a modern browser reaches an HTML tag, it assumes that you forgot to close your <svg>
tag, and closes it for you. Which means that (a) your SVG is corrupted and (b) your fallback text is visible in modern browsers. The only reason you can use links (<a>
tags) is because they are valid tags in SVG, but don’t draw anything themselves.
Another approach here would be to start with HTML text, and swap it out with inline SVG should you detect with JavaScript it is supported. Imagine a “like” button in HTML:
<button aria-label="Like">
<span class="inline-svg" data-xlink="#icon-heart">♥</span> Like
</button>
The span with the ♥ is the fallback there. The data attribute is what we’ll use for a fallback, in this case, a <svg>
/<use>
element.
if (supportsSvg()) { // see test above
var inlineSvgs = document.querySelectorAll('span.inline-svg');
for(i = 0; i < inlineSvgs.length; i++) {
var span = inlineSvgs[i];
var svgns = "http://www.w3.org/2000/svg";
var xlinkns = "http://www.w3.org/1999/xlink";
var svg = document.createElementNS(svgns, "svg");
var use = document.createElementNS(svgns, "use");
// Prepare the <use> element
use.setAttributeNS(xlinkns, 'xlink:href', span.getAttribute('data-xlink') );
// Append it
svg.appendChild(use);
// Prepare the SVG
svg.setAttribute('class', "inline-svg");
// Set a title if necessary.
if (span.getAttribute('title')) {
svg.setAttribute('title', span.getAttribute('title'));
}
// Inject the SVG
span.parentNode.insertBefore(svg, span);
// Remove fallback
span.remove();
}
}
HTML formatted-text fallback within inline SVG
You can use HTML text-formatting markup without ruining the SVG by including it within SVG <desc>
(description) tags, which allow content from other namespaces.
<svg viewBox="-20 -20 40 40">
<desc>
<p class="warning">
Fallback text.
</p>
</desc>
<!-- ... SVG content ... -->
</svg>
Things to note:
- The main purpose of the
<desc>
tag is to provide alternative text description. Make sure the content of the tag makes sense to a screen reader. - In order for
<desc>
to be recognized by accessibility technologies on all browsers, it should be at the top of the SVG, right after the<title>
. - You should also be able to use an SVG
<metadata>
tag to include all sorts of arbitrary markup without affecting the SVG or its alternative text. However, this doesn’t work in browsers tested (they insert implicit</svg>
end tags). - The SVG text content still gets included in the fallback text.
You could even include an <img>
tag with a fallback image within the <desc>
and it would display correctly in the old browsers without breaking your SVG in the new browsers. Don’t do this! Although modern browsers do not display the fallback image, they do download it, and we’re always trying to avoid double-downloads.
background-image
fallback for inline SVG
A possibility for an inline <svg>
fallback is to set a background-image that is only used if the browser doesn’t support inline <svg>
. So, using the test above, you give yourself a class to work with:
if (!supportsSvg()) {
document.documentElemement.classList.add("no-svg");
<em>// or even .className += " no-svg"; for deeper support</em>
}
Then say you’re using some inline SVG like this:
<button>
<svg class="icon icon-key">
<use xlink:href="#icon-key"></use>
</svg>
Sign In
</button>
You could use that new class to apply a background-image if needed:
html.no-svg .icon-key {
background: url(fallback-key.png) no-repeat;
}
Demo:
You’ll have to create the PNG and size it yourself. There is a walkthrough article here on CSS-Tricks about using Grunticon to automate all this.
For the deepest possible support, you’d wrap the <svg>
in a <div>
and apply the background to that, so even if the browser totally rejects the SVG element, the fallback will still work on the known <div>
element.
<img>
fallback within inline SVG
We covered this above in “the <image>
trick” section for inline fallbacks, but since this is really using inline SVG, let’s cover it in more detail here. To get an image fallback in SVG, without extra downloads, you need a way of including an image:
- That old browsers will recognize as valid HTML
- That the HTML 5 parser will allow inside
<svg>
- That modern browsers will not interpret as an image to download.
As strange as it may seem, the SVG <image>
element serves this purpose. The SVG <image>
element is used to embed other image files within SVG. However, within HTML, every browser tested recognizes <image>
as a non-standard synonym for <img>
. In SVG, you specify the URL of the image file with the xlink:href
attribute. In HTML, you specify it with the src
attribute.
In most browsers, therefore, it is sufficient to include an <image>
tag with a src
attribute (pointing to your fallback image) inside your inline SVG: the old browsers will download the fallback, the new browsers won’t. Except for Internet Explorer, which downloads the fallback image even when it doesn’t display it. The solution is to put a null xlink:href
attribute on the element. The IE developer tools still show it requesting the fallback, but it aborts almost immediately (<1ms in IE11 or IE10/IE9 emulation mode), before downloading anything. It looks like this:
<svg viewBox="-20 -20 40 40">
<!-- SVG code snipped -->
<image src="fallback.png" xlink:href="" />
</svg>
Now to address that one remaining fallback problem: the text from the SVG that gets dumped to the screen. It somewhat ruins our perfect fallback image replacement. We can use CSS to help us fix this, but it’s not straightforward. We can’t use a simple svg text { display: none; }
because that would ruin the SVG graphic. You could use the detection JavaScript to set classes that will hide or display the text accordingly. But even then, you’ll be stymied by IE8’s non-standard approach to unrecognized tags: they are always treated as empty void tags with no child content. You’ll need to adapt the HTML5 Shiv code to include all the SVG tags if you want to be able to treat them as stylable containers.
A no-JavaScript option is to wrap all your SVG graphics code (except the fallback <image>
) inside an <a>
element. Without a hyperlink destination, an <a>
element in SVG acts exactly like a <g>
grouping element, so there is no harm to your SVG code. And because IE8 recognizes <a>
as a valid container element, any styles applied to it will apply to all the text within it.
So the trick is then to apply styles that will hide an HTML link’s contents, but won’t have an effect on an SVG group. Absolute positioning and hidden overflow do the trick:
.hide-on-fallback {
display: block;
position: absolute;
left: -100%;
height: 0;
width: 0;
overflow: hidden;
}
SVG for Everybody is a JavaScript library for inline SVG fallbacks as well. It’s approach is to start with the most modern possible inline SVG: Inline SVG referenced via <use>
pointing to symbols defined in an external file.
<svg role="img" title="CodePen">
<use xlink:href="spritemap.svg#codepen"></use>
</svg>
If that’s supported, it does nothing. In the case of IE, it Ajax’s for the spritemap and injects it, making it work in modern IE where that normally doesn’t work. In the case of non-SVG supporting browsers, it helps you inject a PNG.
It’s only disadvantage is that the UA test it does isn’t very accurate. You’re probably better off Ajaxing for the SVG and injecting it all the time, which makes for good support and browser caching.
Stuff about Icon Systems
The point of having an icon system is that it makes icons efficient and easy to use. SVG makes for a great icon system. You’ll probably want the fallback for your icon system to still be both efficient and easy to use. Meaning something like the <image>
trick would be a pretty bad fallback. It complicates the markup and means every individual icon is a separate request.
You’ll probably want to approach an icon system fallback with one of these methods:
- Use background images as fallbacks, but have them be a CSS sprite so it still just one request.
- Use an icon font for a fallback. You’ll need an empty element (suggested markup), but @font-face works a lot further back than SVG does so it can be a good fallback.
- Use Grunticon, which has you start with an empty element and progressively enhances up to SVG and handles fallbacks (through a JavaScript test). You can also use Grunticon while still starting with inline SVG if you wish, as detailed in this tutorial
Usually any of these fallbacks require some JavaScript to work. If you have a goal of an SVG icon system fallback with no JavaScript (that works in Android 2.2/2.3), here’s a way.
This requires a little extra markup (the <a>
in addition to the <use>
):
<svg class="icon">
<a class="svg-status"></a>
<use xlink:href="#svg-status" />
</svg>
Then in CSS:
.icon a, .icon + a {
/* styles for every icon */
}
.icon .svg-status,
.icon + .svg-status {
/* styles for this particular icon */
}
The first selector is for Android, the second for IE. The sibling selector is because old IE won’t treat an unrecognized element as a container.
If you don’t like the extra anchor element, and don’t mind losing the fallback when JavaScript is disabled, another strategy is to use JavaScript to inject an element () that both old Android and old IE browsers can style.
The script tests for inline SVG support by seeing if the elements with tag name “use” have the ownerSVGElement
property. Use your icon id
values (from the <use>
element’s xlink:href
attribute) as the class names for the new spans, so no extra markup is required.
Conclusion
You can totally do fallbacks for SVG.
Comprehensive post, well-written Amelia. =)
Although there are a couple more techniques not covered here, I just wanted to note something regarding the fallback for
<object>
section:Doing an
<img>
inside<object>
will cause double image requests in SVG-capable browsers. In order to avoid that, you can do the following:OR even:
OR THIS
Where the second inline
<svg>
would go in the same main page as the embedded<object>
. This will make sure SVg-capable browsers do not request both the SVG and the fallback PNG.Forgot to mention that the reason the second option works is that styles defined inside an inline
<svg>
will/can affect elements defined outside of that SVG, including elements defined inside other inline<svg>
s on the same page.Thanks for the warning, Sara. I hadn’t even thought to test that one — just assumed that it was designed for fallback so it should just work! Silly me.
Thank you sir!
I apologize Amelia. Thank you Ma’am!
First off, awesome article! It is helpful to have all the key fallback techniques in one place.
I do feel like this article needs a “Do you even need a fallback at all?” section though. If you’re targeting only modern browsers that support SVG, or if you’re developing a mobile app that uses an embedded browser that you have control over, you may not need fallbacks at all.
I guess the reader (myself) can just head on over here, but this article at face value leaves the reader with the idea that some form of fallback must be considered.
Thanks Eric. There are a couple mentions of no fallback as an option in the first two sections, either because your target demographic is focused on those with the latest browsers or because the web page still makes sense without the SVG. I suppose it could have been more emphatic. However, the point of the article was more to help developers stuck in the mindset of “SVG is cool, but I can’t use it on my big commercial projects because we need to support IE8”.
Another concern would be for users with images turned off (or, somehow, just SVG turned off) and making sure your site is still usable without the image. This is definitely a larger concern that should be addressed on a holistic level (and not just concerning images,) but these techniques may help in that area.
Yeah, I see those references, and I guess I am just being nit picky. :D
I personally love to feel reassured that there are ways to avoid fallbacks if you have or can establish some sort of control over what browsers will view the site/app. Thanks again for the article!
Excellent Article…learned lots…Thanks for sharing your knowledge…
Great stuff Amelia! I would add that if someone was planning to use the
<object>
syntax, you may also want to look at using the new<picture>
element instead. Sara (above) has a great writeup on that technique here: http://sarasoueidan.com/blog/svg-picture/The tradeoff here is that you would need to be ok with including a polyfill (http://scottjehl.github.io/picturefill/) for browsers that don’t support the new
picture
element yet.Well, never mind. Looks like I missed where you had included that method. :thumbsup:
I may have a possible improvement for hiding the textual leftovers from the inline SVG:
http://codepen.io/Tigt/blog/inline-svg-fallback-without-javascript-take-2
Accessibly hides the SVG content in older browsers
No shiv required
Allows for whatever DOM fallback you like
Avoids the SVG + jQuery IE bug
Allows for using links inside the SVG content (wrapping the fake
<a>
around the real ones can cause problems in older browsers)Allows the SVG to contain
<foreignObject>
It’s not pretty, but it works wonderfully from my testing.
Great approach, Tigt! As you say, your code might be a little more complicated, but it will be more robust with more complicated SVG.
I hadn’t thought about using CSS namespaces as a no-JavaScript way of targetting your styles to modern browsers. My only concern is I’m not sure exactly when support for CSS namespaces was implemented relative to inline SVG support (canIUse fails me here). There might be a few browser versions that support SVG but don’t recognize your namespace-qualified styles. However, I suspect those would be few and far between on the modern web.
Yeah, the only compatibility info I could find was on MDN, but it seems pretty safe from there.
Great post!
I think it’s also important to consider: do we really need to support SVG? and weigh that against the time and effort needed to support it. Can I Use says support for SVG is in the 94% range globally.
I’m working on a project that uses a lot of SVG, including the interactive kind. Looking at the US support (this site only ships to the US and SVG support is slightly higher in the US than global) and looking at the analytics of the landing page that’s been up since the start of the year (which show about 0.2% non-SVG supported browsers), we decided to not support any fallback for the interactive SVG other than some sort of “you need a better browser!” type of message.
For those like 10 people coming in on IE8, even if they bought everything on the site, it’d barely make up for the cost of building and maintaining the fallback.
Sometimes the best option is to just “not support it.”
But for when you do want to, this is a great write-up on how to!
Oh yeah, I used to use the
<desc>
element to store the fallback too, but it ended up being hostile to screen readers, and the HTML5 spec changed to disallow any sort of markup within it. Sad, because that would have been useful for its intended purpose.Can you explain the “being hostile to screen readers”? Were you having problems when using simple marked-up text (like in the example), or were you trying to embed complex content such as images?
Double-checking the spec, it may be possible that screen-readers don’t announce
<desc>
elements that aren’t at the top of their parent element. I guess I’d have to test it!Ah, yes, that’s a nasty caveat with
<title>
and<desc>
— they need to come first, before any child content. It was a performance issue at the time the spec was written; browsers didn’t want to have to search for these elements. I suspect the performance issues are less relevant now, but even if we remove that requirement from SVG 2, you’ll still need it for backwards compatibility.I’ll correct the example to use that structure.
What about the ol’ onError attribute trick?
It’s there. (Chris Coyier added it in for completeness.) In the section on replacing SVG-as-
<img>
.I would lean to having a single function that finds all your
<img>
elements and swaps them, as opposed to having to write the code in an attribute for each one. It is also probably slightly faster (caveat: I haven’t tested this) to use the JS test instead of waiting for the SVG file request to return and error out on the old browsers.Hi,
If you
– don’t mind detecting “Browsers” instead of “features”
– Are using SVGs in tags
Then one very simple way is to use an .htaccess (or whatever floats your web) to redirect old browsers to the png version of your svg.
Yes, a server-side sniff test is always another option, no matter how much it is frowned upon.
I would like to think that it is highly unlikely that any new browsers will come on the scene that don’t support SVG, so the above code is probably not going to bite you.
Great article. I was just searching for “best” solution for svg fallback. Now I have a few alternatives as well. :) I love you guys.
For inline SVG with an image replacement, I typically do this:
if (!Modernizr.inlinesvg) {
$(‘#containing-div’).prepend(”);
}
As long as I’ve removed the xmlns link in the SVG, IE8 just ignores the SVG block without screwing up the page, and loads the .png fallback.
That was supposed to be:
I wanted a dependency free solution that worked on as many browsers as possible for the logo of the site.
So far I’ve opted for
<object>
using inline base64 data uri of the svg and inside a<div>
that has a class to load the PNG fallback. The CSS for that is set in an inline<style>
in the head.I’ve added wrapping
<div>
with role=”img” and aria-label for accessibility.I’ve been weighing fallback options for an icon system and this incredibly helpful. Thanks so much!
I was set to use the
<image>
trick, but now I’ll be looking into Tigt’s Pen, as I want a no-JavaScript solution.The only thing I was hoping to see is what method you personally favor. (It seems Chris “goes commando” with no fallback.)
I can’t say I have a personal favourite because I don’t do a lot of production work — my main target audience is usually people learning SVG!
More generally, however, I think it really depends on what you’re trying to communicate. I don’t think you need an identical image on every browser, but you do need to get the message across and to make sure your site is still functional (as much as possible).
So, a basic alt text is often going to be enough for icons. For complex data viz, providing a text/table version of the data is something I’d recommend for many reasons, but it isn’t going to convey all the information of a well-designed chart. I tend to lean to the no-JS solutions as much as possible, so the
<image>
trick would be my next go-to. And probably I would now use Tigt’s approach to hiding the extra text.