Search code examples
svgstyling

SVG <use> ignores Gradient styling


I have a svg file that does not render gradient in the <use> tag. Why is it behaving differently, and how can I fix it?

SVG File:

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100"
    style="stroke:#000000;stroke-width:2"
    xml:space="preserve"
     id="pb_svg_1">
   <rect width="50" height="50" style="fill: url(#lg1)"/>
   <defs>
      <linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
         <stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"></stop>
         <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"></stop>
      </linearGradient>
   </defs>
</svg>

These are 3 methods I render the svg. 2 work but the one I need does not:

<!-- Does NOT include gradient style -->
<svg width="100" height="100">
    <use href="12.svg#pb_svg_1" width="100" height="100"/>
</svg>
<!-- Gradient style works! -->
<div>
    <object data="12.svg" width="100" height="100"></object>
</div>
<!-- Gradient style works! -->
<div style="width: 100px;height: 100px">
    <embed src="12.svg"/>
</div>

I expect the use element to render the file as it does when the svg is on the same page.

EDIT: It does work in firefox and does not work in chrome and edge


Solution

  • Workaround: define gadients in an inlined svg

    Move the gradient <defs> to an inlined hidden <svg>.
    It's important to hide this svg via zero width and height properties like width:0; height:0;position:absolute;.
    display:none or visibility:hidden will remove/disable gradients, clip paths etc.

    <!-- HTML svg use instance -->
    <svg width="100" height="100" viewBox="0 0 100 100">
        <use href="#pb_svg_1" style="fill: url(#lg1); stroke:#000000;stroke-width:2 "/>
    </svg>
    
    <!-- Inline svg: hidden gradient definition -->
    <svg style="width:0; height:0; position:absolute;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
       <defs>
          <linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
             <stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"/>
             <stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
          </linearGradient>
       </defs> 
    </svg>
    
    <!-- External svg: 12.svg -->
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <symbol  id="pb_svg_1" viewBox="0 0 100 100">
       <rect x="0" y="0" width="50" height="50" /></symbol>
    </svg>

    Workaround 2: inline external use references

    If refactoring all svg assets isn't feasible – change your embedding method.

    2.1 via fetch

    HTML

    <!-- HTML svg use instance -->
    <svg width="100" height="100" viewBox="0 0 100 100">
        <use  href="12.svg#pb_svg_1" />
    </svg> 
    

    Js

    inlineExternalUse();
    
    function inlineExternalUse(){
        let extSvgs = document.querySelectorAll('use');
        if(extSvgs.length){
            extSvgs.forEach(function(item, i){
                let href = item.getAttribute('href') ? item.getAttribute('href') : item.getAttribute('xlink:href');
                // change href to inline reference
                let hrefNew = '#'+href.split('#')[1];
                item.setAttribute('href', hrefNew);
            
                fetch(href)
                  .then(response => response.text() )
                  .then(data => {
                    //inline ext svg
                    let parser = new DOMParser();
                    let svgInline = parser.parseFromString(data, "application/xml").querySelector('svg');
                    svgInline.setAttribute('aria-hidden', 'true')
                    svgInline.style.width=0;
                    svgInline.style.height=0;
                    svgInline.style.position='absolute';
                    document.body.appendChild(svgInline);
                  });
            });
        }
    }
    

    2.2: via native web component

    See @Danny '365CSI' Engelman's article "〈load-file〉Web Component, add external content to the DOM" or this answer "How to change the color of an svg element?"