414

Is it possible to use an inline SVG definition in CSS?

I mean something like:

.my-class {
  background-image: <svg>...</svg>;
}
Manngo
  • 14,066
  • 10
  • 88
  • 110
akaRem
  • 7,326
  • 4
  • 29
  • 43
  • 1
    Beware that proposed solutions won't work for CSS images, HTML `` tags and other cases if the SVG is a mix of several images (unless embedded), see [background image SVG with mask using external image not working](https://stackoverflow.com/questions/27339334/background-image-svg-with-mask-using-external-image-not-working), and specifically [restrictions on SVG used as an image](https://longsonr.wordpress.com/2013/10/23/restrictions-on-svg-used-as-an-image/). – Skippy le Grand Gourou Jan 23 '18 at 12:26

11 Answers11

572

Yes, it is possible. Try this:

body {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><linearGradient id='gradient'><stop offset='10%' stop-color='%23F00'/><stop offset='90%' stop-color='%23fcc'/> </linearGradient><rect fill='url(%23gradient)' x='0' y='0' width='100%' height='100%'/></svg>");
}

(Note that the SVG content needs to be url-escaped for this to work, e.g. # gets replaced with %23.)

This works in IE 9 (which supports SVG). Data-URLs work in older versions of IE too (with limitations), but they don’t natively support SVG.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Raab
  • 34,778
  • 4
  • 50
  • 65
  • 10
    The only browser in which it seems to work nicely is Safari (5.1.4). In Opera 11.62 the gradient is black, in IE 9 and Firefox 12 it's white. In Chrome 19, it works UNLESS you specify the width/height of the SVG in % units. I'd say it's more of an oddity than a real feature. It's a cool find though. – toniedzwiedz May 26 '12 at 18:40
  • 4
    Right... still I'm anxious to see the looks on my coworkers' faces when I show them a cute little monster like this so thanks again for showing it's possible. I just went to the standard specification and stated it was virtually impossible, which turned out to be a mistake (sort of) – toniedzwiedz May 26 '12 at 19:05
  • 24
    The "browser incompatibility" here is mostly just a lack of proper URL escaping, everything inside `url()` should be url-escaped. See http://jsfiddle.net/6WAtQ/ for an example that works just fine in Opera, Firefox and Safari. – Erik Dahlström May 27 '12 at 19:46
  • 3
    Is there any compatibility difference between base64 encoded svg to non-base64? Base64 bloats my css file, I'm thinking in just use inline svgs.. – enapupe Apr 15 '13 at 13:39
  • To escape the characters, I used this python code, which seemed to work: `urllib.quote('', safe='<>=-. "/\:;')` – rattray Jan 31 '14 at 03:15
  • In web dev console, you can use escape it as: `copy("data:image/svg+xml," + encodeURI("").replace(/#/g, "%23"));` – mems Mar 17 '14 at 10:01
  • 1
    Url-escaping the svg no longer works. This method does not work at all in IE (try [this demo](http://jsfiddle.net/6WAtQ/635/)); but if you use base 64 encoding, it works in all modern browsers. See @MikeTommasi's answer for more details. – Mottie Nov 25 '14 at 04:19
  • 7
    Note, the standard way to specify the character set is with ";charset=UTF-8" instead of ";utf8". http://tools.ietf.org/html/rfc2397 – Keith Shaw Oct 05 '15 at 18:23
  • 1
    I had to remove ";utf8" and URL escape everything after the comma to get it to work in IE11. – Keith Shaw Oct 05 '15 at 18:24
  • 1
    Some characters need to be escaped. See this tool: https://codepen.io/jakob-e/pen/doMoML – Sphinxxx Aug 31 '17 at 03:18
  • But how do you scale it? – Quolonel Questions Dec 05 '17 at 13:27
  • @QuolonelQuestions using the [same techniques](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Backgrounds_and_Borders/Scaling_background_images) used to scale other background images, such as `background-size` – Scribblemacher Mar 26 '18 at 11:47
  • 7
    Here's a very nice tool for online conversion of an svg (source code) directly into css property: https://yoksel.github.io/url-encoder/ – lehel Apr 12 '18 at 08:35
  • down voted because it didn't work, as explained above you can try to url escape things, or you can just use the base64 encoding technique below. – sec0ndHand Aug 31 '20 at 23:14
  • "# gets replaced with %23", that thing saved me, thank you! – Vasil Kostadinov Jul 06 '22 at 10:33
  • I replaced double quotes " with single quotes ' and # with %23. Hint: you can use the browser address input for checking. – Sergey Beloglazov Aug 09 '23 at 08:25
343

If any of you have been going crazy trying to use inline SVG as a background, the escaping suggestions above does not quite work. For one, it does not work in IE, and depending on the content of your SVG the technique will cause trouble in other browsers, like FF.

If you base64 encode the svg (not the entire url, just the svg tag and its contents! ) it works in all browsers. Here is the same jsfiddle example in base64: http://jsfiddle.net/vPA9z/3/

The CSS now looks like this:

body { background-image: 
    url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+PGxpbmVhckdyYWRpZW50IGlkPSdncmFkaWVudCc+PHN0b3Agb2Zmc2V0PScxMCUnIHN0b3AtY29sb3I9JyNGMDAnLz48c3RvcCBvZmZzZXQ9JzkwJScgc3RvcC1jb2xvcj0nI2ZjYycvPiA8L2xpbmVhckdyYWRpZW50PjxyZWN0IGZpbGw9J3VybCgjZ3JhZGllbnQpJyB4PScwJyB5PScwJyB3aWR0aD0nMTAwJScgaGVpZ2h0PScxMDAlJy8+PC9zdmc+");

Remember to remove any URL escaping before converting to base64. In other words, the above example showed color='#fcc' converted to color='%23fcc', you should go back to #.

The reason why base64 works better is that it eliminates all the issues with single and double quotes and url escaping

If you are using JS, you can use window.btoa() to produce your base64 svg; and if it doesn't work (it might complain about invalid characters in the string), you can simply use https://www.base64encode.org/.

Example to set a div background:

var mySVG = "<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><linearGradient id='gradient'><stop offset='10%' stop-color='#F00'/><stop offset='90%' stop-color='#fcc'/> </linearGradient><rect fill='url(#gradient)' x='0' y='0' width='100%' height='100%'/></svg>";
var mySVG64 = window.btoa(mySVG);
document.getElementById('myDiv').style.backgroundImage = "url('data:image/svg+xml;base64," + mySVG64 + "')";
html, body, #myDiv {
  width: 100%;
  height: 100%;
  margin: 0;
}
<div id="myDiv"></div>

With JS you can generate SVGs on the fly, even changing its parameters.

One of the better articles on using SVG is here : http://dbushell.com/2013/02/04/a-primer-to-front-end-svg-hacking/

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Mike Tommasi
  • 3,431
  • 1
  • 11
  • 3
  • 4
    Thanks, man. The solution with Base64 worked excellent, while I ran into trouble with the accepted answer. – Marcel Jun 13 '14 at 22:08
  • 2
    You saved my life. I had a SVG border image that was working in chrome but not on FF. Now it works! :D – Papipo Nov 19 '14 at 11:51
  • Helped me as well (after loosing time trying out the accepted answer) - this should definitely be the accepted answer. – Katai Feb 17 '16 at 13:04
  • If this is still not working for you - make sure you have `xmlns` attribute set to `svg` element ***before*** you base64-encode like `...` browsers sometimes chunk it without it when svg is directly in HTML - this is NOT the case :) – jave.web Feb 16 '20 at 00:54
  • 2
    In case somebody is still looking at this answer 6+ years later: You probably shouldn't base64 SVGs https://css-tricks.com/probably-dont-base64-svg/ – Volker E. Mar 19 '20 at 05:36
  • Another issue I would like to point out is that, base64 strings are usually larger than their SVG equivalents – wentjun May 27 '20 at 02:31
  • 4
    To reply to the "You probably shouldn't base64 SVGs" comment(s). If you are wild beast base64 encoding an entire svg library, reconsider your decisions so far and don't base64 the world. However, if you are making a module or plugin and want to embed very small SVG instead of including an entirely new file, please do that. Including one base64 encoded SVG instead of another directory dependency is worlds easier for you and someone who uses your small module / plugin. – Jesse Apr 03 '21 at 01:25
  • Is it possible also with `clip-path: url('...')`? I tried to achieve that, but without success. – Marcin Goławski Apr 04 '22 at 08:22
  • this is the best answer, too match hassle working with encoding problems, this is simple and does the job the first time – bingeScripter Jul 07 '23 at 10:01
56

My solution was https://yoksel.github.io/url-encoder/ You just simply insert your svg and getting back background-image code

Anna Vlasenko
  • 916
  • 5
  • 8
  • 3
    This worked. Quick, super simple, and very clean. – RockyK Dec 07 '20 at 19:33
  • in case it helps anyone, i tried this and it worked for me, and also added `background-size: cover` property and svg seems to stretch proportionately to its container. – user1063287 Nov 23 '21 at 04:35
47

For people who are still struggling, I managed to get this working on all modern browsers IE11 and up.

base64 was no option for me because I wanted to use SASS to generate SVG icons based on any given color. For example: @include svg_icon(heart, #FF0000); This way I can create a certain icon in any color, and only have to embed the SVG shape once in the CSS. (with base64 you'd have to embed the SVG in every single color you want to use)

There are three things you need be aware of:

  1. URL ENCODE YOUR SVG As others have suggested, you need to URL encode your entire SVG string for it to work in IE11. In my case, I left out the color values in fields such as fill="#00FF00" and stroke="#FF0000" and replaced them with a SASS variable fill="#{$color-rgb}" so these can be replaced with the color I want. You can use any online converter to URL encode the rest of the string. You'll end up with an SVG string like this:

    %3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20494.572%20494.572%27%20width%3D%27512%27%20height%3D%27512%27%3E%0A%20%20%3Cpath%20d%3D%27M257.063%200C127.136%200%2021.808%20105.33%2021.808%20235.266c0%2041.012%2010.535%2079.541%2028.973%20113.104L3.825%20464.586c345%2012.797%2041.813%2012.797%2015.467%200%2029.872-4.721%2041.813-12.797v158.184z%27%20fill%3D%27#{$color-rgb}%27%2F%3E%3C%2Fsvg%3E


  1. OMIT THE UTF8 CHARSET IN THE DATA URL When creating your data URL, you need to leave out the charset for it to work in IE11.

    NOT background-image: url( data:image/svg+xml;utf-8,%3Csvg%2....)
    BUT background-image: url( data:image/svg+xml,%3Csvg%2....)


  1. USE RGB() INSTEAD OF HEX colors Firefox does not like # in the SVG code. So you need to replace your color hex values with RGB ones.

    NOT fill="#FF0000"
    BUT fill="rgb(255,0,0)"

In my case I use SASS to convert a given hex to a valid rgb value. As pointed out in the comments, it's best to URL encode your RGB string as well (so comma becomes %2C)

@mixin svg_icon($id, $color) {
   $color-rgb: "rgb(" + red($color) + "%2C" + green($color) + "%2C" + blue($color) + ")";
   @if $id == heart {
      background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20494.572%20494.572%27%20width%3D%27512%27%20height%3D%27512%27%3E%0A%20%20%3Cpath%20d%3D%27M257.063%200C127.136%200%2021.808%20105.33%2021.808%20235.266c0%204%27%20fill%3D%27#{$color-rgb}%27%2F%3E%3C%2Fsvg%3E');
   }
}

I realize this might not be the best solution for very complex SVG's (inline SVG never is in that case), but for flat icons with only a couple of colors this really works great.

I was able to leave out an entire sprite bitmap and replace it with inline SVG in my CSS, which turned out to only be around 25kb after compression. So it's a great way to limit the amount of requests your site has to do, without bloating your CSS file.

Davy Baert
  • 535
  • 4
  • 5
  • 1
    Btw, correct me if I'm wrong but `rgb(255,0,0)` should become `rgb(255%2C0%2C0)` once encoded. – Capsule Aug 15 '16 at 07:30
  • @Capsule Yes you're right. It works without encoding the RGB string, but it's probably better if you do. – Davy Baert Aug 20 '16 at 01:28
  • I didn't say you shouldn't encode the RGB string, I said the way you encode it is incorrect. Parenthesis remain the same once URL-encoded, but commas should be `%2C` – Capsule Aug 25 '16 at 11:59
  • 2
    I meant that I don't encode the RGB string and it still works. But encoding it as you mentioned is probably better. – Davy Baert Aug 26 '16 at 19:37
  • Did you try to URLencode `#` into `%23`? Maybe this is what's missing to make it work in Firefox without the need to use `rgb` colors – Capsule Sep 05 '16 at 11:58
  • 2
    Well, actually, I just tested and `%23ff0000` works fine for `#ff0000` in Firefox – Capsule Sep 05 '16 at 12:27
  • 1
    @Capsule I don't know what's happening, but the %23ff0000 is the ONLY method that works for me on both Chrome and FF. #ff0000 doesn't work, and neither do the RGB(255,0,0) and rgb(255%2C0%2C0) methods. – Ideogram Nov 25 '16 at 13:39
  • 2
    A method (including SCSS code) that requires less encoding: https://codepen.io/jakob-e/pen/doMoML – Sphinxxx Aug 31 '17 at 03:14
  • When passing colors as a mixin argument (I wanted to be able to pass whatever value, e.g. `white`), I was finding even rgb() values to get converted to hex first by (Dart) Sass, but then they weren't escaped for use in the mixin's SVG "url". It seems workarounds were (a) pass hex without `#` (and only accept hash-less hex), (b) pass hex values as `#{'%'}23NNNNNN` (escaping the percent in `%23` since Sass choked on that too), (c) use nonsense like `rgba(N,N,N,.9999)` (since Sass also converted alphas of 1 to hex), or (d) string conversion junk. Perhaps there's a Sass setting to change this? – Max Starkenburg Nov 05 '21 at 22:58
34

On Mac/Linux, you can easily convert a SVG file to a base64 encoded value for CSS background attribute with this simple bash command:

echo "background: transparent url('data:image/svg+xml;base64,"$(openssl base64 < path/to/file.svg)"') no-repeat center center;"

Tested on Mac OS X. This way you also avoid the URL escaping mess.

Remember that base64 encoding an SVG file increase its size, see css-tricks.com blog post.

Simon East
  • 55,742
  • 17
  • 139
  • 133
araks
  • 40,738
  • 8
  • 34
  • 39
  • 3
    To the readers: please comment your opinion instead of just voting down, so this answer can be improved with your collaboration! Collaboration is essential in Q&A sites like this. Thank you! – araks Jun 01 '15 at 12:54
  • 3
    @LorDex the link you provided in your comment is the same that's in my answer :) – araks Apr 16 '18 at 12:47
  • What is the reason in base4 conversion if plaintext svg takes less space and is ready to use inline? – Alex Khimich Aug 29 '21 at 12:20
12

I've forked a CodePen demo that had the same problem with embedding inline SVG into CSS. A solution that works with SCSS is to build a simple url-encoding function.

A string replacement function can be created from the built-in str-slice, str-index functions (see css-tricks, thanks to Hugo Giraudel).

Then, just replace %,<,>,",', with the %xxcodes:

@function svg-inline($string){
  $result: str-replace($string, "<svg", "<svg xmlns='http://www.w3.org/2000/svg'");
  $result: str-replace($result, '%', '%25');
  $result: str-replace($result, '"', '%22');
  $result: str-replace($result, "'", '%27');
  $result: str-replace($result, ' ', '%20');
  $result: str-replace($result, '<', '%3C');
  $result: str-replace($result, '>', '%3E');
  @return "data:image/svg+xml;utf8," + $result;
}

$mySVG: svg-inline("<svg>...</svg>");

html {
  height: 100vh;
  background: url($mySVG) 50% no-repeat;
}

There is also a image-inline helper function available in Compass, but since it is not supported in CodePen, this solution might probably be useful.

Demo on CodePen: http://codepen.io/terabaud/details/PZdaJo/

Lea Rosema
  • 1,100
  • 16
  • 31
  • 1
    I also created a pen which allows you to convert svg strings into a proper css background value : http://s.codepen.io/LukyVj/debug/693cbcc30258bf67b8c30047cce060eb So, basically, you paste your `` into the top textarea, and it will directly output the sanitized path within a `url()` value. – LukyVj May 17 '16 at 11:27
  • 1
    This worked awesome. Thank you. One note. You need to use ;charset=utf8 to get this to work in IE. – Daniel Lefebvre May 03 '17 at 19:19
8

I found one solution for SVG. But it is work only for Webkit, I just want share my workaround with you. In my example is shown how to use SVG element from DOM as background through a filter (background-image: url('#glyph') is not working).

Features needed for this SVG icon render:

  1. Applying SVG filter effects to HTML elements using CSS (IE and Edge not supports)
  2. feImage fragment load supporting (firefox not supports)

.test {
  /*  background-image: url('#glyph');
    background-size:100% 100%;*/
    filter: url(#image); 
    height:100px;
    width:100px;
}
.test:before {
   display:block;
   content:'';
   color:transparent;
}
.test2{
  width:100px;
  height:100px;
}
.test2:before {
   display:block;
   content:'';
   color:transparent;
   filter: url(#image); 
   height:100px;
   width:100px;
}
<svg style="height:0;width:0;" version="1.1" viewbox="0 0 100 100"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
     <g id="glyph">
          <path id="heart" d="M100 34.976c0 8.434-3.635 16.019-9.423 21.274h0.048l-31.25 31.25c-3.125 3.125-6.25 6.25-9.375 6.25s-6.25-3.125-9.375-6.25l-31.202-31.25c-5.788-5.255-9.423-12.84-9.423-21.274 0-15.865 12.861-28.726 28.726-28.726 8.434 0 16.019 3.635 21.274 9.423 5.255-5.788 12.84-9.423 21.274-9.423 15.865 0 28.726 12.861 28.726 28.726z" fill="crimson"/>
     </g>
    <svg id="resized-glyph"  x="0%" y="0%" width="24" height="24" viewBox="0 0 100 100" class="icon shape-codepen">
      <use xlink:href="#glyph"></use>
    </svg>
     <filter id="image">
       <feImage xlink:href="#resized-glyph" x="0%" y="0%" width="100%" height="100%" result="res"/>
       <feComposite operator="over" in="res" in2="SourceGraphic"/>
    </filter>
 </defs>
</svg>
<div class="test">
</div>
<div class="test2">
</div>

One more solution, is use url encode

var container = document.querySelector(".container");
var svg = document.querySelector("svg");
var svgText = (new XMLSerializer()).serializeToString(svg);
container.style.backgroundImage = `url(data:image/svg+xml;utf8,${encodeURIComponent(svgText)})`;
.container{
  height:50px;
  width:250px;
  display:block;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: contain;
}
<svg  height="100" width="500" xmlns="http://www.w3.org/2000/svg">
    <ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" />
</svg>
<div class="container"></div>
Alex Nikulin
  • 8,194
  • 4
  • 35
  • 37
6

Inline SVG coming from 3rd party sources (like Google charts) may not contain XML namespace attribute (xmlns="http://www.w3.org/2000/svg") in SVG element (or maybe it's removed once SVG is rendered - neither browser inspector nor jQuery commands from browser console show the namespace in SVG element).

When you need to re-purpose these svg snippets for your other needs (background-image in CSS or img element in HTML) watch out for the missing namespace. Without the namespace browsers may refuse to display SVG (regardless of the encoding utf8 or base64).

mp31415
  • 6,531
  • 1
  • 44
  • 34
4

If you're using postcss you can try the postcss-inline-svg plugin https://www.npmjs.com/package/postcss-inline-svg

.up {
    background: svg-load('img/arrow-up.svg', fill: #000, stroke: #fff);
}
.down {
    background: svg-load('img/arrow-down.svg', fill=#000, stroke=#fff);
}
Dr Nic
  • 2,072
  • 1
  • 15
  • 19
2

Done programatically based on the approach taken by the already mentioned https://github.com/yoksel/url-encoder/ :

// Svg (string)

const hexagon = `
<svg
width="100"
height="20"
viewBox="0 0 100 20"
xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient
      id="redyel"
      gradientUnits="objectBoundingBox"
      x1="0"
      y1="0"
      x2="1"
      y2="1"
    >
      <stop offset="0%" stop-color="#ff0000" />
      <stop offset="100%" stop-color="#ffff00" />
    </linearGradient>
  </defs>
  <polygon
    points="0,10 5,0 95,0 100,10 95,20 5,20"
    fill="#eee"
    stroke="url(#redyel)"
  />
</svg>
`

// svgToBackgroundImage

const symbols = /[%#()<>?[\\\]^`{|}]/g;

const newLine = /\r?\n/;

const notEmptyString = (str) => str.length;
const trim = (str) => str.trim();

const toOneLine = (str) =>
  str.split(newLine).filter(notEmptyString).map(trim).join(" ");

function addNameSpace(svgString) {
  if (svgString.indexOf(`http://www.w3.org/2000/svg`) < 0) {
    svgString = svgString.replace(
      /<svg/g,
      `<svg xmlns="http://www.w3.org/2000/svg"`
    );
  }

  return svgString;
}

function encodeSVG(svgString) {
  svgString = svgString.replace(/>\s{1,}</g, `><`);
  svgString = svgString.replace(/\s{2,}/g, ` `);

  // Using encodeURIComponent() as replacement function
  // allows to keep result code readable
  return svgString.replace(symbols, encodeURIComponent);
}

const svgToBackgroundImage = (svgString) =>
  `url('data:image/svg+xml,${encodeSVG(addNameSpace(toOneLine(svgString)))}')`;

// DOM

const element = document.querySelector("#hexagon");
element.style.backgroundImage = svgToBackgroundImage(hexagon);
#hexagon {
  width: 100px;
  height: 20px;
}
<div id="hexagon"/>
Izhaki
  • 23,372
  • 9
  • 69
  • 107
  • 1
    The whole point of this question is how to do it inline. E.g. if you don't have an event hook to some element that gets added to the page and you have to override it with pure CSS. – cchamberlain Mar 30 '21 at 22:10
-4

You can also just do this:

<svg viewBox="0 0 32 32">
      <path d="M11.333 13.173c0-2.51 2.185-4.506 4.794-4.506 2.67 0 4.539 2.053 4.539 4.506 0 2.111-0.928 3.879-3.836 4.392v0.628c0 0.628-0.496 1.141-1.163 1.141s-1.163-0.513-1.163-1.141v-1.654c0-0.628 0.751-1.141 1.419-1.141 1.335 0 2.571-1.027 2.571-2.224 0-1.255-1.092-2.224-2.367-2.224-1.335 0-2.367 1.027-2.367 2.224 0 0.628-0.546 1.141-1.214 1.141s-1.214-0.513-1.214-1.141zM15.333 23.333c-0.347 0-0.679-0.143-0.936-0.404s-0.398-0.597-0.398-0.949 0.141-0.689 0.398-0.949c0.481-0.488 1.39-0.488 1.871 0 0.257 0.26 0.398 0.597 0.398 0.949s-0.141 0.689-0.398 0.949c-0.256 0.26-0.588 0.404-0.935 0.404zM16 26.951c-6.040 0-10.951-4.911-10.951-10.951s4.911-10.951 10.951-10.951c6.040 0 10.951 4.911 10.951 10.951s-4.911 10.951-10.951 10.951zM16 3.333c-6.984 0-12.667 5.683-12.667 12.667s5.683 12.667 12.667 12.667c6.984 0 12.667-5.683 12.667-12.667s-5.683-12.667-12.667-12.667z"></path>
</svg>