ChatGPT解决这个技术问题 Extra ChatGPT

How to style SVG with external CSS?

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).

Try svg { fill:red; } or give your path a class name. E.g. <path class="socIcon" d="M28.44 ..... /> this should do the trick.
You can use either <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="styles.css"/> or <style>@import url(styles.css);</style> inside the <svg> element.

M
Mahozad

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.


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.
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.
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.
shame you can't load a stylesheet inside an svg from a URL
@clayRay you will be able to do it that way once SVG2 is complete draft w3.org/TR/SVG2/styling.html#LinkElement
W
Wenfang Du

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>

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
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> `
J
Jacek Mamot

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.


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?
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.
Note that svg loaded through an tag will not load external content (e.g., stylesheets).
@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.
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.
P
Pieter van den Ham

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.


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 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
S
Simon White

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.


I came here hoping to find something like this. Woo, hotness!
L
Ludovic Kuty

It should be possible to do by first inlining the external svg images. The code below comes from replace all SVG images with inline SVG by Jess Frazelle.

$('img.svg').each(function(){
  var $img = $(this);
  var imgID = $img.attr('id');
  var imgClass = $img.attr('class');
  var imgURL = $img.attr('src');
  $.get(imgURL, function(data) {
    // Get the SVG tag, ignore the rest
    var $svg = $(data).find('svg');
    // Add replaced image's ID to the new SVG
    if (typeof imgID !== 'undefined') {
      $svg = $svg.attr('id', imgID);
    }
    // Add replaced image's classes to the new SVG
    if (typeof imgClass !== 'undefined') {
      $svg = $svg.attr('class', imgClass+' replaced-svg');
    }
    // Remove any invalid XML tags as per http:validator.w3.org
    $svg = $svg.removeAttr('xmlns:a');
    // Replace image with new SVG
    $img.replaceWith($svg);
  });
});

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
this is legendary
v
vhanahrni

A very quick solution to have dynamic style with an external css stylesheet, in case you are using the <object> tag to embed your svg.

This example will add a class to the root <svg> tag on click on a parent element.

file.svg :

<?xml-stylesheet type="text/css" href="../svg.css"?>
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="">
  <g>
   <path/>
  </g>
 </svg>

html :

<a class="parent">
  <object data="file.svg"></object>
</a>

Jquery :

$(function() {
  $(document).on('click', '.parent', function(){
    $(this).find('object').contents().find('svg').attr("class","selected");
  }
});

on click parent element :

 <svg xmlns="http://www.w3.org/2000/svg" viewBox="" class="selected">

then you can manage your css

svg.css :

path {
 fill:none;
 stroke:#000;
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

.selected path {
 fill:none;
 stroke:rgb(64, 136, 209);
 stroke-miterlimit:1.41;
 stroke-width:0.7px;
}

doesn't seem to work, could you add a working example?
Y
Yuvraj Patil

For External styles

For Internal Styles

Note: External Styles will not work if you include SVG inside <img> tag. It will work perfectly inside <div> tag


<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="main.css" type="text/css"/> also works
R
Robert Longson

When used in an <image> tag SVG must be contained in a single file for privacy reasons. This bugzilla bug has more details on exactly why this is so. Unfortunately you can't use a different tag such as an <iframe> because that won't work as a link so you'll have to embed the CSS in a <style> tag within the file itself.

One other way to do this would be to have the SVG data within the main html file i.e.

<a href='http://youtube.com/...' target='_blank'>
  <svg 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>
</a>

You could style that with an external CSS file using the HTML <link> tag.


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.
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.
F
Fordi

What works for me: style tag with @import rule

<defs>
    <style type="text/css">
        @import url("svg-common.css");
    </style>
</defs>

T
Tino Costa 'El Nino'

@leo here is the angularJS version, thanks again

G.directive ( 'imgInlineSvg', function () {

return {
    restrict : 'C',
    scope : true,
    link : function ( scope, elem, attrs ) {

        if ( attrs.src ) {

            $ ( attrs ).each ( function () {
                var imgID    = attrs.class;
                var imgClass = attrs.class;
                var imgURL   = attrs.src;

                $.get ( imgURL, function ( data ) {

                    var $svg = $ ( data ).find ( 'svg' );
                    if ( typeof imgID !== 'undefined' ) {
                        $svg = $svg.attr ( 'id', imgID );
                    }

                    if ( typeof imgClass !== 'undefined' ) {
                        $svg = $svg.attr ( 'class', imgClass + ' replaced-svg' );
                    }

                    $svg = $svg.removeAttr ( 'xmlns:a' );

                    elem.replaceWith ( $svg );

                } );

            } );
        }

    }

}

} );

S
Susobhan Das

In my case, I have applied display:block in outer class.

Need to experiment, where it fits.

Inside inline svg adding class and style does not even remove the above white-space.

See: where the display:block gets applied.

<div class="col-3 col-sm-3 col-md-2  front-tpcard"><a class="noDecoration" href="#">
<img class="img-thumbnail img-fluid"><svg id="Layer_1"></svg>
<p class="cardtxt">Text</p>
</a>
</div>

The class applied

   .front-tpcard .img-thumbnail{
        display: block; /*To hide the blank whitespace in svg*/
    }

This worked for me. Inner svg class did not worked


F
Frans

I know its an old post, but just to clear this problem... you're just using your classes at the wrong place :D

First of all you could use

svg { fill: red; }

in your main.css to get it red. This does have effect. You could probably use node selectors as well to get specific paths.

Second thing is, you declared the class to the img-tag.

<img class='socIcon'....

You actually should declare it inside your SVG. if you have different paths you could define more of course.

<?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 class="myClassForMyPath" d="M28.44......./>
</g>
</svg>

Now you could change the color in your main.css like

.myClassForMyPath {
    fill: yellow;
}

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 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.
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.
sure you tried my example correct? i mean, included a css file from external ? <?xml-stylesheet href="stylesheets/main.css" type="text/css"?>
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).
M
MayeulC

As described in answers here and in other related questions, stylesheets only apply to the current DOM. As such, you need to make the svg part of the document's DOM, by inlining it inside the html, or including it inside the DOM using javascript.

That's what I ended up doing there:

<object type="image/svg+xml" data="illustration.svg"
onload="this.parentNode.replaceChild(this.contentDocument.documentElement, this);">
</object>

While that solution works really well for me, only use it on documents you control, as inline loading an svg from an untrusted source gives that source the ability to include at least arbitrary scripts, css and other elements inside your HTML, breaking the sandbox.

I haven't investigated how well caching works with this, but it should work as well as with img tags, given that the javascript function is ran after the element loads. Feel free to edit this.

If javascript is disabled, the svg is not included into the DOM, and the style is not applied, so make sure the default style is usable. CSS custom properties (variables) with fallbacks work quite well for that use-case.


C
Community

"I am actually going to change the colors of these images based on what color scheme the user has chosen for my site." - Jordan 10 hours ago

I suggest you to use PHP for this. There's really no better way to do this without icon fonts, and if you resist using them, you could try this:

<?php

    header('Content-Type: image/svg+xml');
    echo '<?xml version="1.0" encoding="utf-8"?>';
    $color = $_GET['color'];

?>
<!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 fill="<?php echo $color; ?>" d="M28.44..."/>
    </g>
</svg>

And later you could use this file as filename.php?color=#ffffff to get the svg file in the desired color.


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).
H
HTMLGUY1999

This method will work if the svg is viewed within a web browser but as soon as this code is uploaded to the sever and the class for the svg icon is coded as if it was a background image the color is lost and back to the default color. Seems like the color can not be changed from the external style sheet even though both the svg class for the color and the top layer class for the display and position of the svg are both mapped to the same directory.