32

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

decho
  • 494
  • 1
  • 4
  • 9
  • You'd have to get the object document using javascript and then manipulate its DOM, you can do it with CSS only. – Robert Longson Mar 07 '14 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 Mar 07 '14 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 Mar 16 '15 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 >>, then style with your class .blah {...}. – Robot Jun 16 '15 at 07:33
  • Does this answer your question? [How to style SVG with external CSS?](https://stackoverflow.com/questions/18434094/how-to-style-svg-with-external-css) – MayeulC Jul 06 '22 at 13:46

4 Answers4

28

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.

Robot
  • 3,811
  • 1
  • 13
  • 14
  • 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 Sep 23 '16 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 Oct 22 '17 at 10:58
  • 2
    This answer is no longer 100% true. You can include and style external files via `` and ``. See [Ben Crook's answer](https://stackoverflow.com/a/39663457/1494454). – totymedli Feb 26 '20 at 04: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

Ben Crook
  • 607
  • 9
  • 15
  • Thank you. This is nice, however Safari still does not seem to support it. https://caniuse.com/#feat=svg-fragment – Rolf Oct 22 '17 at 11:07
  • 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. https://css-tricks.com/svg-fragment-identifiers-work/ Explains it well although I've only skim read it. – Ben Crook Oct 23 '17 at 07:21
  • And for any browsers that do not support external SVGs I recommend this polyfill - https://github.com/Keyamoon/svgxuse – Ben Crook Oct 23 '17 at 07:24
  • Note that [xlink:href as been deprecated](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href) in favour of [href](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/href). – BenMorel Aug 18 '18 at 12:11
  • 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.)* – Fabien Snauwaert May 06 '19 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]"`. – Fabien Snauwaert May 06 '19 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](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use): "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 `` to include the SVG and creating variations of the SVG file.) – Fabien Snauwaert Feb 01 '20 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 Feb 01 '20 at 22:27
  • Here is a [short tutorial](https://w3bits.com/svg-sprites/) with a [working demo](https://w3bits.com/demo/svg-sprites/). – totymedli Feb 26 '20 at 04:50
  • ^^ I advise creating the sprites automatically with something like Gulp/Webpack rather than having a design manually update the sprite. – Ben Crook Feb 26 '20 at 09:16
3

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.

MayeulC
  • 1,628
  • 17
  • 24
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.

Shubham
  • 21,300
  • 18
  • 66
  • 89