<svg style="display:none;">
  <defs>
    <symbol id="icon-heart" viewBox="0 0 32 32" fill="currentColor">
      <path d="M0 10 C0 6, 3 2, 8 2 C12 2, 15 5, 16 6 C17 5, 20 2, 24 2 C30 2, 32 6, 32 10 C32 18, 18 29, 16 30 C14 29, 0 18, 0 10  "></path>
    </symbol>
    <symbol id="icon-close" viewBox="0 0 32 32" fill="currentColor">
      <path d="M4 8 L8 4 L16 12 L24 4 L28 8 L20 16 L28 24 L24 28 L16 20 L8 28 L4 24 L12 16 z "></path>
    </symbol>
    <symbol id="icon-hamburger" viewBox="0 0 32 32" fill="currentColor">
      <path d="M3 8 A3 3 0 0 0 9 8 A3 3 0 0 0 3 8 M12 6 L28 6 L28 10 L12 10z M3 16 A3 3 0 0 0 9 16 A3 3 0 0 0 3 16 M12 14 L28 14 L28 18 L12 18z M3 24 A3 3 0 0 0 9 24 A3 3 0 0 0 3 24 M12 22 L28 22 L28 26 L12 26z "></path>
    </symbol>
  </defs>
</svg>

<h1>Progressive SVGs</h1>

<div class="buttons" data-codeblock>
  
  <button aria-label="Like">
    <span class="inline-svg" data-xlink="#icon-heart">♥</span>
  </button>

  <button aria-label="Close">
    <span class="inline-svg" data-xlink="#icon-close">Close</span>
  </button>

  <button aria-label="Menu">
    <span class="inline-svg" data-xlink="#icon-hamburger" >&#x2261;</span>
  </button>

  <p><a href="#">Do you <span class="inline-svg" data-xlink="#icon-heart" title="like">♥</span> me?</a></p>

  <button aria-label="Like">
    <span class="inline-svg" data-xlink="#icon-heart">♥</span> Like
  </button>

</div>


## Working theory:

Most inline SVGs don't need fallbacks, they're icon candy paired with assistive text and/or they are decorative (like icon-fonts). This aims at **progressively enhancing critical fallbacks** when absolutely necessary. So, we take a span, with a little extra meta and replace it with an SVG `<use>` element when it appears enhanceable.

How it looks in IE8

![](https://s3-us-west-2.amazonaws.com/s.cdpn.io/17/Screenshot_2014-12-17_17.19.56.png)


## Approach

I try to scope the Image Replacement (IR) to either text or unicode. There are 5 examples here:

1. A Like `button` with a Unicode "BLACK HEART SUIT" (♥) fallback. This heart is generally supported. Added an `aria-label` on the `button` to avoid screenreaders stumbling on wonky Unicode. VoiceOver failed on reading SVG `title` attributes in a `button`.
1. A Close button with "Close" as a text fallback. The Unicode "MULTIPLICATION SIGN" (×) character has decent support, but I wanted an example where you might fall back to text. Same ARIA approach as above.
1. A Hamburger button with fallback Unicode "IDENTICAL TO" (≡) character. Same ARIA approach, but very necessary due to the desired label being "Menu" which is very different than the character's meaning.
1. A link with a Unicode heart (♥) fallback. Adding a `title` attribute here actually improved the accessibility for both SVG and fallback span.
1. A Like button with "Like" text. This doesn't really even require a fallback.

## Rules

- Use similar Unicode when you can get away with it. 
  - See [Unify](https://unicode.johnholtripley.co.uk/all/support) by John Ripley to guage how well a character is supported across devices and screenreaders. 
- There's a bit of CSS you'll need to make sure old Windows uses 'Arial Unicode MS' as a fallback font with Unicode support.
- A lot of Zach Leatherman's [Bulletproof Icon Fonts](https://www.filamentgroup.com/lab/bulletproof_icon_fonts.html) applies here.
- Must be Accessible. Avoid wonky Unicode reading:
  - Add `aria-label` to parent `button` elements.
  - Use `title` attributes otherwise.
- If you used an external SVG (e.g., `some.svg#icon-hamburger`), you might be able to save legacy browsers the double download.

<small>SVGs from <a href="http://geomicons.com/">Geomicons</a></small>
View Compiled
// General Style
html, body {
  background: #f0f0f0;
}

h1 {
  text-align: center;
}

body {
  font-family: sans-serif;
  padding: 5%;
  line-height: 1.6em;
}

img {
  max-width: 100%;
  height: auto;
}

.buttons {
  text-align: center;
}

pre {
  background: #fff;
  padding: 1em;
  max-height: 20em;
  overflow: auto;
  margin-bottom: 5%;
}

button {
  font-size: 1em;
  line-height: 1em;
}

// Inline SVG Code
// ====================================================
.inline-svg {
  display: inline-block;
}

span.inline-svg {
  // Unicode chars need a Unicode font on old browsers.
  font-family: 'Arial Unicode MS', Arial, sans-serif;
}

svg.inline-svg {
  // Tailor to suite your needs.
  width: 1em;
  height: 1em;
  vertical-align: text-top;
}
View Compiled
(function(window, document){
  // Feature Test
  // TODO: Could be improved to detect INLINE only.
  var supportsSvg = function(){
    return document.implementation && document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
  };

  // Enhance!
  if(supportsSvg) {
    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);
      
      // 6 Minute Abs™, for your DOM.
      span.remove();
    }
  }
})(window, document, undefined);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. //cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js
  3. //codepen.io/davatron5000/pen/tzJKh.js