Home > Back-end >  How to define CSS style for ALL types of SVG'S structure?
How to define CSS style for ALL types of SVG'S structure?

Time:01-18

I have many SVG's on my page, that I imported as (in React):

import { ReactComponent as Logo } from "./../../../images/example.svg";

And them I use them this way:

<div className='someClassName activeClassName?'>
  <Logo />
</div>

But all of them are different in their structure. What do I mean? Some of SVG's are looks like:

<svg>
  <path/>
</svg>

some like:

<svg>
  <g>
     <g>
        <path/>
     </g>
  </g>
</svg>

some looks like:

<svg>
  <g>
     <circle/> 
     <triangle/>
  </g>
</svg>

And there are millions of types like this. I have a 'activeClassName' which fill SVG in different color, when it's active, but to make it work with all my SVG's, I have to describe my classname styles kinda like this:

&--active {
      svg {
        fill: $primaryBlue !important;
        path {
          fill: $primaryBlue !important;
        }
        g {
          fill: $primaryBlue !important;
          g path {
            fill: $primaryBlue !important;
          }
        }
      }
    }

This looks awful. How can I change, for example, the fill option for all of those SVG's? Please, help me... thanks

CodePudding user response:

As @Robert Longson and @chrwahl pointed out:
removing fill attributes from your svg child elements is recommended.
Not sure, how you could "pre/postprocess" your imported svg component.

In plain js you could easily query your child elements and remove attributes like so:

let svgAsset = document.querySelector(".svgAsset");
// query child elements – maybe includeother svg primitives like circles/rects
let svgChildEls = svgAsset.querySelectorAll("g, path, circle, rect, polygon");


function removeFills(els = svgChildEls) {
  els.forEach(function (el, i) {
      el.removeAttribute("fill");
  });
}

function addElClass(els = svgChildEls) {
  els.forEach(function (el, i) {
    let nodeName = el.nodeName.toLowerCase();
    if(nodeName!='g'){
       el.classList.add("svgChild");
    }
  });
}

function toggleActive(){
  svgAsset.classList.toggle('svgActive');
  svgAsset.classList.toggle('svgInactive');
}
svg{
  display:inline-block;
  width:200px;
}

/* inactive */
.svgInactive{
    fill: #ccc;
}

.svgActive{
  fill: orange;
}

.svgActive
.svgChild{
  fill: blue;
}
<p>
  <button onclick="toggleActive()">toggle active</button>
  <button onclick="removeFills()">Remove fill attributes</button>
  <button onclick="addElClass()">Add element Class</button>
</p>
<div >
  <svg  viewBox="0 0 100 100">
    <path id="path0" fill="red" d="M0 0 l50 0 l0 50 l-50 0 z" />
    <path id="path1"  fill="green" d="M33 33 l50 0 l0 50 l-50 0 z" />
    <g fill="purple">
      <circle id="" cx="66.666%" cy="33.333%" r="25%" fill="none" stroke="#000" stroke-width="2" />
    </g>
  </svg>
</div>
In the above example I've also included <g> elements and other shape primitives like polygons:

let svgChildEls = svgAsset.querySelectorAll("g, path, circle, rect, polygon");

You benefit from a lower css specificity – so you don't need nested selectors like

svg g path{ ... }

Manually checking and optimizing your svg source material is always the best approach, since you can't expect graphics from different sources to have a coherent structure.
E.g svgs generated by GUI applications tend to have slightly quirky markup including way to many <g> nesting, unsused or too many transforms (making it hard to get or manipulate x/y offsets) etc.

  •  Tags:  
  • Related