0

I downloaded some svgs from bootstrap and added them to my project. Here is an example of person-fill.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16">
  <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z"/>
</svg>

If I place this content directly into html, I can manipulate colors through fill and stroke properties.

However, in order to reuse this svg, I added it to my project as svg file, and then I reference it in html through img tag:

<img class="user-icon" src="/images/svg/person-fill.svg" title="User profile" />

This works, however, now I am unable to change colors. Basically, I could change the background color by setting background-color attribute on user-icon class, since this svg is transparent, but I am unable to truly set the fill and stroke attributes.

Anyone knows if its possible to control svg's fill and stroke attributes (preferentially through css class), without the need to copy svg content directly to html? If not, is there some other way where I do not need to copy svg everywhere I use it?

Goran
  • 6,328
  • 6
  • 41
  • 86

2 Answers2

1

It takes a bit of 'getting the hang of it'. I created a HTML/CSS commented example using your SVG.

But, most importantly, answering your question: you cannot access SVG CSS properties when using it as an <img>, because the browser will treat it as a graphics element and no longer as a XML document. To be able to access those properties you will need to use the svg/use structure described.

The basics:

  • Define a main in-doc SVG

  • Which contains a list (<defs>) of <symbol>, each with a unique id

  • Reference a specific SVG somewhere inside a parent element as you would use an <img>:

    <div class="svg-using-element">
       <svg>
          <use href="#some-ID">
       </svg>
    </div>
    
  • Edit modifiable SVG properties inside your regular CSS, like fill, stroke, etc.

  • You can even create a <symbol> that uses a list of nested symbols and use that in your <div>. Nesting can go several levels deep with good planning.

    <symbol id="id-1">...</symbol>
    <symbol id="id-2">...</symbol>
    <symbol id="id-3">...</symbol>
     ...
    <symbol id="id-N">...</symbol>
    
    <symbol id="compound-symbol">
       <use href="id-1" />
       <use href="id-2" />
       <use href="id-3" />
    </symbol>"
    
    <symbol id="nested-symbol">
       <use href="compound-symbol" />
       <use href="id-N" />
    </symbol>
    

Here is some Codepen demo I posted almost 2 years ago: svg-burger-menu-patches.

The snippet clarifies things better:

 /* * { outline: 1px dashed } /* for debugging */

/* An element that 'uses' a predefined, in-doc SVG symbol */
.svg-user {
    stroke: hsl(0deg, 100%, 50%, 0.5); /* transparent red */
    fill  : black;

    width: 50vw; /* online size */
    height: 50vw;

    margin: 0 auto;
}

.svg-user>svg {
    display: block; overflow: hidden;

    width : 100%; /* stretch to fill parent */
    height: 100%;
}
<div class="svg-user">
    <svg>
        <use href="#person-fill"/><!-- use the symbol by referencing its ID -->
    </svg>
</div>

<!-- Remove color, size and other attributes from the main <svg> and hide it from view -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
    <!--
        - Move all SVGs you want to use in your page inside <defs></defs> as <symbol>
        - Define a viewbox per <symbol>
        - Give the symbol an ID that can be <use>d
        - Remove attributes you want to control from CSS, except x,y,r, etc.
        - Use (CSS) classes where required
        - A <style> block inside this main SVG will work as expected too
        - As would <script>
        - After all, it is an XML file...
    -->
    <defs>
        <!-- A list of various SVG symbols, each with their own unique ID -->
        <symbol id="person-fill" class="bi bi-person" viewBox="0 0 16 16">
            <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z"/>
        </symbol>

    </defs>
</svg>
Rene van der Lende
  • 4,992
  • 2
  • 14
  • 25
  • This looks like something that can do what I need, will test it to see if it behaves friendly with my current CSP, and will get let you know if this approach is OK. I also found another way, although looks like a bit more complex than yours, using CSS filters https://css-tricks.com/the-many-ways-to-change-an-svg-fill-on-hover-and-when-to-use-them/ – Goran Dec 25 '22 at 07:37
  • @Goran, I would like to know the result, yes. Just remember, if you are required to use external SVGs you will *have* to resort to a workaround like using **filter**. You could also use the SVG as a `background-image` and overlay it with a `gradient(..)` using mix/background-blend-mode. Like `.svg-user { background-image: url(..svg..), radial-gradient(..); background-blend-mode: normal } .svg-user:hover { background-blend-mode: ..mode 1.., ..mode 2.. }`. Let me know if you want me to extend the answer with this... – Rene van der Lende Dec 25 '22 at 14:24
  • I did some small modifications to fit my purpose, but it works OK, as long as inline styles are not used, CSP did not complain, Thanks. – Goran Dec 30 '22 at 09:33
  • @Goran glad it works for you! Here's [a different answer](https://stackoverflow.com/a/74925597/2015909) I gave last week with an other `` case. – Rene van der Lende Dec 30 '22 at 14:09
1

See Dev.To Post: <load-file> Web Component


Use a modern, native W3C standard Web Component <load-svg>

  • it reads the SVG as text
  • adds SVG to shadowDOM as DOM element
  • moves the style element from lightDOM to shadowDOM
    So style is only applied to one SVG

<load-svg src="//svg-cdn.github.io/heart.svg">
  <style>
    svg { height:180px } path { fill: red }
  </style>
</load-svg>
<load-svg src="//svg-cdn.github.io/heart.svg">
  <style>
    svg { height:180px } path { fill: green }
  </style>
</load-svg>

<script>
  customElements.define('load-svg', class extends HTMLElement {
    async connectedCallback() {
      this.attachShadow({mode: "open"})
          .innerHTML = await (await fetch(this.getAttribute("src"))).text();
      this.shadowRoot.append(...this.childNodes);
    }
  });
</script>

More complex example: How to make an svg interactive to gather comments/annotations on depicted elements

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49