htmlsvgspritelinear-gradientssprite-sheet

linearGradient not working for SVG sprites


I am trying to combine all svg icons into one sprite file like this for organization, caching and other purposes. But for some reason, linearGradient is not being applied

  <?xml version="1.0" encoding="utf-8"?>
  <svg version="1.1" id="icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
   y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
    <!-- other icons -->
    <!-- ... -->
    <g id="toggle">
      <path d="M4,7.992c-2.206,0-4-1.794-4-4c0-2.206,1.794-4,4-4h10c2.206,0,4,1.794,4,4c0,2.206-1.794,4-4,4H4z"/>
      <circle fill="url('#toggle-linear-gradient')" cx="14" cy="3.992" r="3"/>
      <defs>
        <linearGradient id="toggle-linear-gradient" gradientUnits="userSpaceOnUse" x1="14" y1="0.9922" x2="14" y2="6.9922">
          <stop offset="0" stop-color="#FFFFFF"/>
          <stop offset="1" stop-color="#8C8C8B"/>
        </linearGradient>
      </defs>
    </g>
  </svg>

Now, in file preview I can see circle with applied gradient correctly

in svg file preview

When I try to import it into my HTML like this

<svg>
  <use href="#toggle" xlink:href="#toggle"></use>
</svg>

the circle is not being colored with defined gradient

in the page

When I moved <linearGradient> out of svg file, it worked, but why?

<svg>
  <linearGradient id="toggle-linear-gradient" gradientUnits="userSpaceOnUse" x1="14" y1="0.9922" x2="14" y2="6.9922">
    <stop offset="0" stop-color="#FFFFFF"/>
    <stop offset="1" stop-color="#8C8C8B"/>
  </linearGradient>
  <use href="#toggle" xlink:href="#toggle"></use>
</svg>

with linearGradient outside svg file

Even tho it worked, the problem is, that this kills the purpose of keeping all the icons in one file if some styling can only be defined from the outside. Displaying same icon will force me defining same gradient multiple times. Would love to have everything in the same file. Any ideas or insights?

P.S. importing as <img> displays the circle gradient correctly.


Solution

  • Pretty sure, you have hidden your main svg asset file by display:none.

    If you change this to visibility:hidden it should work:

    function unhide(){
      document.querySelector('.dsp-non').classList.remove('dsp-non');
    }
    .svgAssetHidden{
      visibility:hidden;
      position:absolute;
      width:0px;
      height:0px;
      overflow:hidden;
    }
    
    .svgIcon{
      display:inline-block;
      width:10em;
    }
    
    .dsp-non{
      display:none;
    }
    <button onclick="unhide()">remove display:none</button>
    
    <h3>visibility:hidden</h3>
    <svg class="svgIcon" viewBox="0 0 50 20">
      <use href="#toggle" href="#toggle"></use>
    </svg>
    
    <h3>display:none</h3>
    <svg class="svgIcon" viewBox="0 0 50 20">
      <use href="#toggle2" href="#toggle"></use>
    </svg>
    
    <svg class="svgAssetHidden" id="icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
       y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
        <!-- other icons -->
        <!-- ... -->
        <g id="toggle">
          <path d="M4,7.992c-2.206,0-4-1.794-4-4c0-2.206,1.794-4,4-4h10c2.206,0,4,1.794,4,4c0,2.206-1.794,4-4,4H4z"/>
          <circle fill="url('#toggle-linear-gradient')" cx="14" cy="3.992" r="3"/>
          <defs>
            <linearGradient id="toggle-linear-gradient" gradientUnits="userSpaceOnUse" x1="14" y1="0.9922" x2="14" y2="6.9922">
              <stop offset="0" stop-color="#FFFFFF"/>
              <stop offset="1" stop-color="#8C8C8B"/>
            </linearGradient>
          </defs>
        </g>
      </svg>
    
    
    <svg class="svgAssetDspNon dsp-non" id="icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
       y="0px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
        <!-- other icons -->
        <!-- ... -->
        <g id="toggle2">
          <path d="M4,7.992c-2.206,0-4-1.794-4-4c0-2.206,1.794-4,4-4h10c2.206,0,4,1.794,4,4c0,2.206-1.794,4-4,4H4z"/>
          <circle fill="url('#toggle-linear-gradient2')" cx="14" cy="3.992" r="3"/>
          <defs>
            <linearGradient id="toggle-linear-gradient2" gradientUnits="userSpaceOnUse" x1="14" y1="0.9922" x2="14" y2="6.9922">
              <stop offset="0" stop-color="#FFFFFF"/>
              <stop offset="1" stop-color="#8C0000"/>
            </linearGradient>
          </defs>
        </g>
      </svg>

    Hiding a svg by display:none will also break some other features like filters.