34

I am trying to manipulate an external .svg file via CSS.

HTML

<body>
    <div class="mysvg">
    <img src="decho.svg" alt="decho" width="200px"></img>
    </div>
</body>

CSS

div.mysvg img {
    opacity: .3;
    transition: opacity 1s linear 0s;
}
    div.mysvg img:hover {
    opacity: 1;
}

This code works for opacity, but not for fill or other svg specific attributes like stroke. I am aware I can't do that with an img tag, but I've been looking for hours and I can't find the correct way to do it with svg or object.

So basically, my questions is, how do I achieve the same result as the code which I linked, but to be able to manipulate fill, stroke etc. properties and it must be an external file, not just an inline svg code pasted in the html.

If someone is able to show me the correct way to do it, I'd be most grateful. Thanks.


EDIT:

I managed to do it by adding a css inside the .svg file itself. It must be right after the svg opening tag.

<svg ...>
<style type="text/css" media="screen">  
    <![CDATA[  
    g {  
        fill: yellow;  
        stroke: black;  
        stroke-width: 1;
        transition: fill 1s linear 0s;
    }
    g:hover {
        fill: blue;
    }
    ]]>  
</style> 
<g>
    <path ...>
</g>
</svg>

You also need to insert it as an object in the html, otherwise it won't work.

<object data="decho.svg" type="image/svg+xml">

Hopefully this helps to someone looking for an answer like mine in future. This is what helped me http://www.hongkiat.com/blog/scalable-vector-graphic-css-styling/.

5
  • You'd have to get the object document using javascript and then manipulate its DOM, you can do it with CSS only. Commented Mar 7, 2014 at 14:39
  • Thanks for your answer. I've been reading that I could add the CSS directly in to the svg file, do you think that's a good solution to my problem?
    – decho
    Commented Mar 7, 2014 at 14:42
  • 1
    Have you got a way to manipulate the svg size using css? I'm not sure if it's possible, since I haven't found in the net a way to do so. I am using the svg code in an html file that I am embedding in the main page.
    – j4v1
    Commented Mar 16, 2015 at 19:59
  • @j4v1 If you check the link in my answer, there are two possible ways to manipulate the svg's insides' using css. One is to convert all your svg's into a font, then manipulate them using font-size and :hover psuedo's. The other way is to load the svg's into html at the top of your page as svg defs then render them as <svg class="blah"><use ...link here></></>, then style with your class .blah {...}.
    – Robot
    Commented Jun 16, 2015 at 7:33
  • Does this answer your question? How to style SVG with external CSS?
    – MayeulC
    Commented Jul 6, 2022 at 13:46

5 Answers 5

30

This is in my opinion the greatest flaw in svg: sandboxing.

Svg files are sandboxed: in their own document, which is why a typical 'fill:' style will not apply. Likewise, the css you write in your svg will not apply to the rest of your site.

Adding css directly to an svg: Not a good solution as you will end up rewriting the css in every svg you use.

The real solution: An "icon-system". Svg font-face or svg sprites. Read more about them here.

The reason opacity works: Opacity applies to the svg object/frame itself, not the contents of the svg (which are inaccessible).

I should also note that no matter how you load those svg's, inline, by reference, in an object, as a background, you will not be able to get inside the sandbox. This is why converting them to a font or using sprites is necessary for using hover, focus, and other effects/transitions.

3
  • It is actually possible as long as the SVG doesn't contain a fill, and you don't use any parent selectors within the CSS (shadow DOM boundaries).
    – Ben Crook
    Commented Sep 23, 2016 at 14:37
  • 1
    Yes, it forces us to include SVG inside the HTML if we want to control it with CSS - forces us to break the separation of content/semantics and graphics or style. I want to know why. OK, there is Ben's solution - which I might start using. However what I had in mind is the basic way of including SVG through img[src]
    – Rolf
    Commented Oct 22, 2017 at 10:58
  • 2
    This answer is no longer 100% true. You can include and style external files via <symbol> and <use>. See Ben Crook's answer.
    – totymedli
    Commented Feb 26, 2020 at 4:53
17

This is possible providing the SVG is hosted on the same domain (thanks @FabienSnauwaert) and it does not have a fill colour defined on itself, and you do not contain a parent selector within the CSS. For example:

I have the following files:

  • icon-sprite.svg (my external sprite of SVGs)
  • buttons.scss
  • test.html

icon-sprite.svg

I have omitted the other SVGs for clarity.

<svg xmlns="http://www.w3.org/2000/svg" style="width:0;height:0;visibility:hidden;">
    <symbol viewBox="0 0 1500 828" id="icon-arrow-3pt-down">
        <title>arrow-3pt-down</title>
        <path d="M1500 0H0l738.9 827.7z"/>
    </symbol>
</svg>

test.html

<button class="button--large">
    Large button
    <svg class="svg" width="20px" height="20px">
        <use xlink:href="icon-sprite.svg#icon-arrow-3pt-down"></use>
    </svg>
</button>

buttons.scss

.svg {
    fill: red;
}

This would not work if I was to use body .svg due to shadow DOM boundaries.

SVG external css styling

See this CSS Tricks article for more info

10
  • 1
    @Rolf I think SVG fragments are different to what I've mentioned above, that seems to be for using a small part of one SVG as a CSS background or inside an image tag. css-tricks.com/svg-fragment-identifiers-work Explains it well although I've only skim read it.
    – Ben Crook
    Commented Oct 23, 2017 at 7:21
  • 1
    Great solution and works in all of Firefox, Chrome and Safari. BEWARE! Even though xlink:href is deprecated, in Safari, the reference must still use the xlink:href attribute – using just href will cause the image not to load. (Posting this as of Safari 12.0.2.) Commented May 6, 2019 at 15:04
  • 3
    While I'm at it, SVGs don't support the alt attribute. An alternative would be this: role="img" aria-label="[title + description]". Commented May 6, 2019 at 15:12
  • 1
    …for this to work, the SVG has to be on the same domain as the hosting page. (So if your site is example.org and you're hosting the SVG on say cdn.example.org, you're out of luck.) Documented here: "For security reasons, browsers could apply the same-origin policy on use elements and could refuse to load a cross-origin URL in the href attribute", which is in practice what's happening at the moment with all big three browsers. (Ended up using a simple <img> to include the SVG and creating variations of the SVG file.) Commented Feb 1, 2020 at 21:51
  • 1
    @FabienSnauwaert Good spot, I didn't think about that case. It makes sense for it to be refused as SVGs can be vulnerable to cross site scripting. I've added that to the answer, thanks.
    – Ben Crook
    Commented Feb 1, 2020 at 22:27
6

I recently ran into this. While SVGs are not part of the DOM for some arbitrary reason, you can move them to the DOM with a bit of javascript:

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

This will replace the <object> with an inline after it has loaded. In case javascript is disabled, it falls back to an <object> tag, and the svg will not be themed. In my case, the styling was for a javascript-controlled dark theme, so having the correct fallback means no theming issue.

Other options considered (xlink is a good one for sprites):

  • Use an external library to load svgs inside the DOM (the above js is simple enough IMO)
  • use svg filters for chroma-keying. That makes svgs more complex to edit, might use more resources to perform the filtering, and is less flexible.

Note that I am not sure of the security implications, better save this for files you control.

0
0

Unfortunately, there's no built-in feature in Web Standards that makes it possible. SVG Symbols is an option but doesn't work with files hosted on CDNs. Additionally, you need to ensure that SVG files are defined properly to make use of <use> tag.

I have created a library, svg-loader, that makes it easier to achieve this. It uses Javascript but it's only 3kb and it's loaded asynchronously, so the impact on performance is negligible. It's plug 'n play, so you don't need to do anything except including the <script> tag.

Here's a Codepen example.

0

I needed this to change the fill color of an element that has a svg background, on hover and given different conditions. I didn't want to add a new svg for each background color needed. In this case, I needed a hexagon background image.

So I created two svgs, one for masking and another for the normal background color.

This is the svg (thanks wikimedia): hexagon.svg

<svg xmlns="http://www.w3.org/2000/svg" height="628" width="726">
   <polygon points="723,314 543,625.769145 183,625.769145 3,314 183,2.230855 543,2.230855 723,314" fill="none" stroke="black" stroke-width="4"/>
</svg>

I copied this svg to another one, exactly the same, only with fill="#fff" (hexagon-masked.svg)

For the css I used (scss)

    .cel {
     background-image: url("/assets/img/hexagon.svg");
     background-size: 100% 100%;
     background-repeat: no-repeat;
     //mask the background
    mask-image: url("/assets/img/hexagon-mask.svg");
    mask-size: 100% 100%;
    mask-repeat: no-repeat;

    background-color: white;

     &:hover {
      background-color: red;
      color: white
     }
    }

This way only the masked part of the .cel div gets the background-color, i.e. the inside of the "hexagon-masked.svg" hexagon that is not transparent. The normal background image is needed to set the stroke.

So, I still need to svgs, but no more than that. I can give it any fill color I want in css.

Not the answer you're looking for? Browse other questions tagged or ask your own question.