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
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 class="svgWrp">
<svg class="svgAsset svgInactive" viewBox="0 0 100 100">
<path id="path0" fill="red" d="M0 0 l50 0 l0 50 l-50 0 z" />
<path id="path1" class="hasClass" 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.
SUMMARY: Some SVG-targeting CSS effects don't work in Chrome & IE (Firefox is fine) on a <use xlink> SVG sprite but the same CSS works absolutely fine when the same SVG code is truly inline.
JSFIDDLE: http://jsfiddle.net/x8vg8k4p/5/
I am currently using <svg><use xlink:href="#symbol-id" /></svg> blocks to call SVG code from an SVG sprite (as an external file) imported via PHP dynamically.
Some CSS effects are applied to the SVG, both as standard and on hover. All these effects work absolutely fine on Firefox, but on both IE and Chrome, they don't:
The fill attribute on the circle does not take effect
The opacity settings on two internal parts of the SVG (the cross by default and the thumb on hover) do not take effect
I believe there is nothing wrong with the CSS as the same code works absolutely fine if the SVG code is inline and not imported via sprite id reference, which the fiddle demonstrates very clearly.
I've struggled for hours, moving bits of code around, adding and removing extra attributes, and not been able to solve this.
What follows are the relevant excerpts of the full code which can be seen on the JSfiddle link - it seems detrimental to readability to include all code here in the question, but I'm happy to edit the question if someone tells me this is bad form.
not working:
<a><svg><use xlink:href="#thumbs-up" /></svg></a>
working:
<a><svg><!-- truly inline SVG code here --></svg></a>
these parts of the CSS are the bits that fail on Chrome and IE:
a svg circle {fill: #4291c2;}
a svg path#cross {opacity: 0;}
a:hover svg circle {fill: #91c142;}
a:hover svg path#cross {opacity: 1;}
a:hover svg g#hand {opacity: 0;}
finally the SVG code itself:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="thumbs-up" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
<g id="hand">
<polygon fill="#FFFFFF" points="64.287,37.926 64.287,71.491 80.925,71.491 73.044,37.926 "/>
<path fill="#FFFFFF" d="M54.425,41.857c0-2.634-2.811-4.295-5.025-5.155c-2.728-1.059-4.069-4.203-1.565-8.379
c2.146-3.58-2.084-8.795-6.628-6.058c-5.205,3.134-4.073,11.161-2.468,15.889c0.61,1.798-0.435,1.743-1.756,1.743
c-1.081,0-5.646,0-5.646,0h-8.469c-0.998,0-3.288,6.399-2.289,6.399h10.729c-0.188,0.5-0.406,1.391-0.619,2.544H19.768
c-1.152,0-1.919,7.2-0.714,7.2h10.859c-0.035,0.842-0.049,1.695-0.038,2.544H19.372c-1.195,0-0.277,6.256,0.803,6.256h10.413
c0.245,0.95,0.561,1.813,0.962,2.544H21.331c-1.294,0,1.405,5.811,3.027,5.811h6.978c4.925,0,13.934,0,17.805,0
c3.872,0,5.378-5.477,11.86-5.477V43.891C61.001,43.891,54.425,44.12,54.425,41.857z"/>
</g>
<path id="cross" fill="#FFFFFF" d="M50.042,54.392L39.967,66.389c-0.659,0.854-1.478,1.281-2.454,1.281
c-0.879,0-1.612-0.306-2.198-0.915c-0.586-0.61-0.879-1.355-0.879-2.234c0-0.781,0.195-1.404,0.586-1.867l11.065-13.199
L35.864,37.311c-0.464-0.536-0.696-1.147-0.696-1.831c0-0.806,0.286-1.531,0.859-2.179c0.572-0.646,1.31-0.971,2.211-0.971
c1.023,0,1.852,0.382,2.485,1.145l9.285,11.188l9.547-11.273c0.586-0.706,1.318-1.06,2.198-1.06c0.781,0,1.49,0.275,2.125,0.824
c0.635,0.55,0.953,1.251,0.953,2.105c0,0.83-0.135,1.404-0.403,1.722L54.021,49.495l10.921,13.158
c0.415,0.463,0.623,1.041,0.623,1.729c0,0.937-0.312,1.718-0.935,2.345c-0.622,0.629-1.337,0.942-2.142,0.942
c-0.952,0-1.782-0.427-2.49-1.282L50.042,54.392z"/>
</symbol>
</svg>
I think the best you can do is to use css custom properties:
:root {
--circle-fill: #4291c2;
--hand-fill: #ffffff;
--cross-fill: #ffffff;
--cross-opacity: 0;
--hand-opacity: 1;
}
a {
text-decoration: none;
display: inline-block;
border: none;
cursor: pointer;
position: relative;
width: 25px;
height: 25px;
margin: 0 2px 0 5px;
}
a, a * {
transition: 0.2s all ease-in-out;
}
a svg {
position: absolute;
left:0;
top:0;
width:100%;
height:100%;
}
a:hover {
opacity: 1.0;
transform: rotate(-5deg) scale(1.5);
}
a:hover .thumb {
--circle-fill: #91c142;
--hand-opacity: 0;
--cross-opacity: 1;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="thumbs-up" viewBox="0 0 100 100">
<circle cx="50" cy="49.999" r="50" style="fill: var(--circle-fill)"/>
<g id="hand">
<polygon style="fill: var(--hand-fill); fill-opacity: var(--hand-opacity)" points="64.287,37.926 64.287,71.491 80.925,71.491 73.044,37.926"/>
<path style="fill: var(--hand-fill); fill-opacity: var(--hand-opacity)" d="M54.425,41.857c0-2.634-2.811-4.295-5.025-5.155c-2.728-1.059-4.069-4.203-1.565-8.379
c2.146-3.58-2.084-8.795-6.628-6.058c-5.205,3.134-4.073,11.161-2.468,15.889c0.61,1.798-0.435,1.743-1.756,1.743
c-1.081,0-5.646,0-5.646,0h-8.469c-0.998,0-3.288,6.399-2.289,6.399h10.729c-0.188,0.5-0.406,1.391-0.619,2.544H19.768
c-1.152,0-1.919,7.2-0.714,7.2h10.859c-0.035,0.842-0.049,1.695-0.038,2.544H19.372c-1.195,0-0.277,6.256,0.803,6.256h10.413
c0.245,0.95,0.561,1.813,0.962,2.544H21.331c-1.294,0,1.405,5.811,3.027,5.811h6.978c4.925,0,13.934,0,17.805,0
c3.872,0,5.378-5.477,11.86-5.477V43.891C61.001,43.891,54.425,44.12,54.425,41.857z"/>
</g>
<path id="cross" style="fill: var(--cross-fill); fill-opacity: var(--cross-opacity)" d="M50.042,54.392L39.967,66.389c-0.659,0.854-1.478,1.281-2.454,1.281
c-0.879,0-1.612-0.306-2.198-0.915c-0.586-0.61-0.879-1.355-0.879-2.234c0-0.781,0.195-1.404,0.586-1.867l11.065-13.199
L35.864,37.311c-0.464-0.536-0.696-1.147-0.696-1.831c0-0.806,0.286-1.531,0.859-2.179c0.572-0.646,1.31-0.971,2.211-0.971
c1.023,0,1.852,0.382,2.485,1.145l9.285,11.188l9.547-11.273c0.586-0.706,1.318-1.06,2.198-1.06c0.781,0,1.49,0.275,2.125,0.824
c0.635,0.55,0.953,1.251,0.953,2.105c0,0.83-0.135,1.404-0.403,1.722L54.021,49.495l10.921,13.158
c0.415,0.463,0.623,1.041,0.623,1.729c0,0.937-0.312,1.718-0.935,2.345c-0.622,0.629-1.337,0.942-2.142,0.942
c-0.952,0-1.782-0.427-2.49-1.282L50.042,54.392z"/>
</symbol>
</svg>
<a><svg><use class="thumb" xlink:href="#thumbs-up"/></svg></a>
You can not address an element that is referenced via use. Here is a detailed answer to your question.
Also, refer to this answer, which talks about a possible workaround.
you have to remove "fill" attribute from your svg icons.
Also my advice is using this one https://github.com/Keyamoon/svgxuse for cross-browser solution
Here is a working demo of a rectangle. I'd like to move the height property to css and well, it doesn't work and gives me a blank. It happens in firefox and chrome.
Is there a different name for it? I don't understand why I can't use a css file. The fill color works.
Working example.
css:
rect {
fill:rgb(0, 0, 255);
/*doesnt work height:100;*/
}
html:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect width="100" height="100" style="stroke-width:1;stroke:rgb(0,0,0)"/>
</svg>
In SVG the x, y, width and height of <rect> elements are attributes rather than CSS properties. Only CSS properties can be styled using CSS.
The width of a <rect> element isn't a CSS property in SVG, it's only usable as an attribute. It's for example like the size of a <select> element in HTML. You can only set it as an attribute.
SVG doesn't have a straightforward support for CSS for setting shape dimensions.
However there's a workaround for rects, which can also be used to generate horizontal and vertical lines:
Set width and height to 1, and use CSS transform: scale(width, height)
Don't specify x,y location, and use transform: translate(x, y)
E.g.
.svg {
width: 100px;
height: 30px;
}
.rectangle {
transform: scale(30, 10);
fill: orange;
}
.horiz-line {
transform: translate(15px,5px) scale(50, 1);
fill: red;
}
.vert-line {
transform: translate(10px, 5px) scale(1, 30);
fill: blue;
}
<svg>
<rect class="rectangle" width="1" height="1" />
<rect class="horiz-line" width="1" height="1" />
<rect class="vert-line" width="1" height="1" />
</svg>
workaround for symmetric rectangles:
rect:hover {
stroke-width: 20 !important;
}
<svg width="100" height="100">
<rect
stroke-width="0"
fill="blue" stroke="blue"
x="30" y="30" width="40" height="40"
/>
</svg>
(darkreader will use different colors for stroke and fill)
Here is a working demo of a rectangle. I'd like to move the height property to css and well, it doesn't work and gives me a blank. It happens in firefox and chrome.
Is there a different name for it? I don't understand why I can't use a css file. The fill color works.
Working example.
css:
rect {
fill:rgb(0, 0, 255);
/*doesnt work height:100;*/
}
html:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect width="100" height="100" style="stroke-width:1;stroke:rgb(0,0,0)"/>
</svg>
In SVG the x, y, width and height of <rect> elements are attributes rather than CSS properties. Only CSS properties can be styled using CSS.
The width of a <rect> element isn't a CSS property in SVG, it's only usable as an attribute. It's for example like the size of a <select> element in HTML. You can only set it as an attribute.
SVG doesn't have a straightforward support for CSS for setting shape dimensions.
However there's a workaround for rects, which can also be used to generate horizontal and vertical lines:
Set width and height to 1, and use CSS transform: scale(width, height)
Don't specify x,y location, and use transform: translate(x, y)
E.g.
.svg {
width: 100px;
height: 30px;
}
.rectangle {
transform: scale(30, 10);
fill: orange;
}
.horiz-line {
transform: translate(15px,5px) scale(50, 1);
fill: red;
}
.vert-line {
transform: translate(10px, 5px) scale(1, 30);
fill: blue;
}
<svg>
<rect class="rectangle" width="1" height="1" />
<rect class="horiz-line" width="1" height="1" />
<rect class="vert-line" width="1" height="1" />
</svg>
workaround for symmetric rectangles:
rect:hover {
stroke-width: 20 !important;
}
<svg width="100" height="100">
<rect
stroke-width="0"
fill="blue" stroke="blue"
x="30" y="30" width="40" height="40"
/>
</svg>
(darkreader will use different colors for stroke and fill)