How to style SVG with external CSS?

144,559

Solution 1

Your main.css file would only have an effect on the content of the SVG if the SVG file is included inline in the HTML:

https://developer.mozilla.org/en/docs/SVG_In_HTML_Introduction

<html>
  <body>
  <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
      <path d="M28.44......."/>
    </g>
  </svg>
</html>

If you want to keep your SVG in files, the CSS needs to be defined inside of the SVG file.

You can do it with a style tag:

http://www.w3.org/TR/SVG/styling.html#StyleElementExample

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
     width="50px" height="50px" viewBox="0 0 50 50">
  <defs>
    <style type="text/css"><![CDATA[
      .socIcon g {
        fill:red;
      }
    ]]></style>
  </defs>
  <g>
    <path d="M28.44......./>
  </g>
</svg>

You could use a tool on the server side to update the style tag depending on the active style. In ruby you could achieve this with Nokogiri. SVG is just XML. So there are probably many XML libraries available that can probably achieve this.

If you're not able to do that, you will have to just have to use them as though they were PNGs; creating a set for each style, and saving their styles inline.

Solution 2

You can do what you want, with one (important) caveat: the paths within your symbol can't be styled independently via external CSS -- you can only set the properties for the entire symbol with this method. So, if you have two paths in your symbol and want them to have different fill colors, this won't work, but if you want all your paths to be the same, this should work.

In your html file, you want something like this:

<style>
  .fill-red { fill: red; }
  .fill-blue { fill: blue; }
</style>

<a href="//www.example.com/">
  <svg class="fill-red">
    <use xlink:href="images/icons.svg#example"></use>
  </svg>
</a>

And in the external SVG file you want something like this:

<svg xmlns="http://www.w3.org/2000/svg">
   <symbol id="example" viewBox="0 0 256 256">
    <path d="M120...." />
  </symbol>
</svg>

Swap the class on the svg tag (in your html) from fill-red to fill-blue and ta-da... you have blue instead of red.

You can partially get around the limitation of being able to target the paths separately with external CSS by mixing and matching the external CSS with some in-line CSS on specific paths, since the in-line CSS will take precedence. This approach would work if you're doing something like a white icon against a colored background, where you want to change the color of the background via the external CSS but the icon itself is always white (or vice-versa). So, with the same HTML as before and something like this svg code, you'll get you a red background and a white foreground path:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="example" viewBox="0 0 256 256">
    <path class="background" d="M120..." />
    <path class="icon" style="fill: white;" d="M20..." />
  </symbol>
</svg>

Solution 3

You can include in your SVG files link to external css file using:

<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>

You need to put this after opening tag:

<svg>
  <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="mystyles.css" type="text/css"/>
  <g>
    <path d=.../>
  </g>
</svg>

It's not perfect solution, because you have to modify svg files, but you modify them once and than all styling changes can be done in one css file for all svg files.

Solution 4

It is possible to style an SVG by dynamically creating a style element in JavaScript and appending it to the SVG element. Hacky, but it works.

<object id="dynamic-svg" type="image/svg+xml" data="your-svg.svg">
    Your browser does not support SVG
</object>
<script>
    var svgHolder = document.querySelector('object#dynamic-svg');
    svgHolder.onload = function () {
        var svgDocument = svgHolder.contentDocument;
        var style = svgDocument.createElementNS("http://www.w3.org/2000/svg", "style");

        // Now (ab)use the @import directive to load make the browser load our css
        style.textContent = '@import url("/css/your-dynamic-css.css");';

        var svgElem = svgDocument.querySelector('svg');
        svgElem.insertBefore(style, svgElem.firstChild);
    };
</script>

You could generate the JavaScript dynamically in PHP if you want to - the fact that this is possible in JavaScript opens a myriad of possibilities.

Solution 5

One approach you can take is just to use CSS filters to change the appearance of the SVG graphics in the browser.

For example, if you have an SVG graphic that uses a fill color of red within the SVG code, you can turn it purple with a hue-rotate setting of 180 degrees:

#theIdOfTheImgTagWithTheSVGInIt {
    filter: hue-rotate(180deg);
    -webkit-filter: hue-rotate(180deg);
    -moz-filter: hue-rotate(180deg);
    -o-filter: hue-rotate(180deg);
    -ms-filter: hue-rotate(180deg);
}

Experiment with other hue-rotate settings to find the colors you want.

To be clear, the above CSS goes in the CSS that is applied to your HTML document. You are styling the img tag in the HTML code, not styling the code of the SVG.

And note that this won’t work with graphics that have a fill of black or white or gray. You have to have an actual color in there to rotate the hue of that color.

Share:
144,559
Jordan H
Author by

Jordan H

*OS Developer

Updated on July 08, 2022

Comments

  • Jordan H
    Jordan H almost 2 years

    I have several SVG graphics I'd like to modify the colors of via my external style sheets - not directly within each SVG file. I'm not putting the graphics in-line, but storing them in my images folder and pointing to them.

    I have implemented them in this way to allow tooltips to work, and I also wrapped each in an <a> tag to allow a link.

    <a href='http://youtube.com/...' target='_blank'><img class='socIcon' src='images/socYouTube.svg' title='View my videos on YouTube' alt='YouTube' /></a>
    

    And here is the code of the SVG graphic:

    <?xml version="1.0" encoding="utf-8"?>
    <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    
    <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 56.69 56.69">
    <g>
        <path d="M28.44......./>
    </g>
    </svg>
    

    I put the following in my external CSS file (main.css):

    .socIcon g {fill:red;}
    

    Yet it has no effect on the graphic. I also tried .socIcon g path {} and .socIcon path {}.

    Something isn't right, perhaps my implementation doesn't allow external CSS modifications, or I missed a step? I'd really appreciate your help! I just need the ability to modify the colors of the SVG graphic via my external stylesheet, but I cannot lose the tooltip and link ability. (I may be able to live without tooltips though.) Thanks!

  • Jordan H
    Jordan H over 10 years
    I cannot put the styles within the files. I am actually going to change the colors of these images based on what color scheme the user has chosen for my site. My current implementation is to add a stylesheet to the main page which overwrites the default styles. I wanted to put the color changes in that file to affect the embedded SVG graphics. But that wouldn't work cuz I have to link to the stylesheet from within the SVG file (unless there's a way to add that link to all SVG files using JavaScript as well). Surely there's a way to allow external SVG files, external CSS, links, and tooltips.
  • Robert Longson
    Robert Longson over 10 years
    It is not possible to do what you want to do because of the browser's security model. You cannot use javascript to manipulate SVG when used as an image. Think of SVG when used as an image as like an animated png or gif file, all in one file and no scripting access.
  • johndodo
    johndodo over 10 years
    Note that this code does not check user input - anything could be supplied as color and your SVG might be rendered in some very interesting ways... Not sure if it affects you, but you should make it a habit to ALWAYS validate user input. Something like this would help: if (!preg_match('/^[#][0-9a-f]{6}$/i', $_GET['color'])) die('Oops!'); (put it somewhere in the start PHP block).
  • msg45f
    msg45f over 9 years
    Does this mean that there is no method to benefit from caching the SVG and applying varied styling? Inline doesn't seem to cache well, while other methods would require creating many version of the image, eliminating any benefit from caching them.
  • ptim
    ptim almost 9 years
    good answer.. i think the caveat should really be: Browser Support, though! good reference (more detail than caniuse): css-tricks.com/svg-fragment-identifiers-work
  • user151496
    user151496 over 8 years
    it's importat to note that this will work only if you have the image hosted on the same domain as the html, or have a specially configured crossdomain policy on the image server. $.get will use ajax and fail to load the image from external server if there's no valid allow-access header
  • Ruskin
    Ruskin about 7 years
    Another way is to encode the SVGs as background-image data uris, with different colours on each version and rely on gzip to reduce file size due to the duplication.
  • Kamel Mili
    Kamel Mili almost 7 years
    hey i actually like you solution and it's working but i need to adopt it on my situtation if you're willing to help os course , i have inside <defs> a style tag i can delete them manually and run this code so it would create a style , is there's a way i can delete the defs then recreate the element like you did or just update it , and also the url got an error url is not defined can you please help , thank you
  • Jeanluca Scaljeri
    Jeanluca Scaljeri over 6 years
    doesn't seem to work, could you add a working example?
  • Tino Costa 'El Nino'
    Tino Costa 'El Nino' over 5 years
    this is legendary
  • SimoneMSR
    SimoneMSR about 5 years
    This is a solution! Actually there is no need to wrap the whole svg content in a symbol element, i.e. you can just give an id to the svg element, so: ` <svg id=example" xmlns="w3.org/2000/svg" viewBox="0 0 256 256"> <path class="background" d="M120..." /> <path class="icon" style="fill: white;" d="M20..." /> </svg> `
  • David Gausmann
    David Gausmann over 4 years
    In my case I wanted to override element styles from the SVG. My CSS didn't work, because the element styles had a higher priority. The most simple solution was it to add an !important to the CSS style for the SVG. Then everything was fine. If you want to avoid !important, you need to move the element styles into the CSS.
  • Bruno  Vincent
    Bruno Vincent about 4 years
    Wow, this is works, yet only 1 upvote...is this solution good for all situations? It' so simple, why isn't this the chosen answer?
  • clayRay
    clayRay almost 4 years
    shame you can't load a stylesheet inside an svg from a URL
  • raddrick
    raddrick almost 4 years
    @clayRay you will be able to do it that way once SVG2 is complete draft w3.org/TR/SVG2/styling.html#LinkElement
  • Frans
    Frans almost 4 years
    Tried this, it doesn't work, like many of the other answers already say. You can't apply styling to classes inside the SVG.
  • Frans
    Frans almost 4 years
    The whole point is centralizing your style definitions. Let's say you have 10 SVGs you want to style. Now you need to copy in a reference to the CSS into all the SVGs that need to be affected. And if the file name / location of your CSS changes, you need to update it in 10 SVGs. A CSS class feels a lot more symbolic than a reference to a physical CSS file.
  • Dwza
    Dwza almost 4 years
    @Frans are you including a svg as a file or do you have your svg source like in the example above? Because I have in mind that this depends on how you use svg. Including by img wont work.
  • Moose Morals
    Moose Morals almost 4 years
    Note that svg loaded through an <img> tag will not load external content (e.g., stylesheets).
  • Frans
    Frans almost 4 years
    Exactly, it only works if you inline the SVG in your HTML. But that is not what your example does. It uses an external (i.e. not inline) SVG. There seems to be no way to style an external SVG with CSS in your HTML.
  • Dwza
    Dwza almost 4 years
    sure you tried my example correct? i mean, included a css file from external ? <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
  • Frans
    Frans almost 4 years
    OK, now I see, you have an <?xml-stylesheet ... ?> declaration inside your SVG. I guess that would work. It's similar to other answers recommending a <link rel="stylesheet" ... > inside the SVG. It also has the same problems (you need to update every single SVG to point to the stylesheet, and any change in name or location of the stylesheet means having to change all the SVGs again).
  • Niklas
    Niklas over 3 years
    Having a style tag as a content of the defs element is permitted. That's at least what the usage notes on Mozilla states. developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
  • Armen Michaeli
    Armen Michaeli over 3 years
    @Frans everything you remarked applies the same way to HTML, so this isn't SVG-specific. In fact it's a very general linking problem -- there are indeed both benefits and drawbacks to mark up links between documents.
  • BBaysinger
    BBaysinger almost 3 years
    I came here hoping to find something like this. Woo, hotness!
  • BarryCap
    BarryCap almost 3 years
    As said by Moose Morals, with this method, we cannot use an <img> tag and load external CSS inside SVG; I would therefore recommend the use of <object>, with the data attribute instead of the src.
  • BarryCap
    BarryCap about 2 years
    <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="main.css" type="text/css"/> also works