I have links with their own SVGs coming from my backend via my API.
I want to overwrite the color when i'm in dark mode and set it to white.
Here is my component that display the SVG :
[...]
<button className="flex items-center fill">
{value.pictogram &&
pictogram(value.pictogram, {
fill: value.pictoFill,
})}
{!props.isMinimized && (
<span className="ml-3">{value.label}</span>
)}
</button>
[...]
export const pictogram: any = (key: string, props: any) => {
return pictogramToComponent[key] ? React.createElement(pictogramToComponent[key], props) : null;
};
And the SVG is like this in the DOM :
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="#C1C1C1"><defs><clipPath id="clip-path-picto-hektor"><rect id="Bold_hyperlink-3" data-name="Bold / hyperlink-3" width="20" height="20" transform="translate(-1862 -1482)" fill="none"></rect></clipPath><clipPath id="clip-path-picto-hektor-2"><rect id="Rectangle_2" data-name="Rectangle 2" width="16" height="16" fill="#525254"></rect></clipPath></defs><g id="ico_Hektor" transform="translate(1862 1482)" opacity="0.3" clip-path="url(#clip-path-picto-hektor)"><g id="Groupe_4" data-name="Groupe 4" transform="translate(-1860 -1480)"><g id="Groupe_3" data-name="Groupe 3" transform="translate(0 0)" clip-path="url(#clip-path-picto-hektor-2)"><path id="Trac\xE9_2" data-name="Trac\xE9 2" d="M13.82,1.071Q12.046,0,8.019,0,4.064,0,2.4.961,0,2.289,0,5.8v4.434q0,3.474,2.4,4.8Q4.064,16,8.019,16a11.986,11.986,0,0,0,5.8-1.072Q16,13.635,16,10.235V5.8q0-3.436-2.18-4.73m.964,6.164a3.75,3.75,0,0,1-3.855,2.889A4.707,4.707,0,0,1,8,8.985a4.707,4.707,0,0,1-2.929,1.139A3.75,3.75,0,0,1,1.216,7.235a.059.059,0,0,1,.107-.049,2.291,2.291,0,0,0,2.022.957A7.284,7.284,0,0,0,6.45,6.8a.548.548,0,0,1,.46-.072L8,7.051l1.09-.319a.548.548,0,0,1,.46.072,7.283,7.283,0,0,0,3.105,1.34,2.292,2.292,0,0,0,2.022-.957.059.059,0,0,1,.106.049" transform="translate(0 0)" fill="#525254"></path></g></g></g></svg>
Any idea what should I change to change the color ?
I tried to add fill class and do this in my CSS :
.fill~svg,
.fill svg,
.fill~path,
.fill path,
.fill~circle,
.fill circle,
.fill~rect,
.fill rect,
.fill~line,
.fill line,
.fill~polyline,
.fill polyline,
.fill~polygon,
.fill polygon,
.fill~text,
.fill text,
.fill~tspan,
.fill tspan,
.fill~g,
.fill g {
fill: #ffffff !important;
}
But nothing change.
Try this:
svg #ico_Hektor {
opacity: 1 !important;
}
svg path {
fill: #000000 !important;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="#C1C1C1"><defs><clipPath id="clip-path-picto-hektor"><rect id="Bold_hyperlink-3" data-name="Bold / hyperlink-3" width="20" height="20" transform="translate(-1862 -1482)" fill="none"></rect></clipPath><clipPath id="clip-path-picto-hektor-2"><rect id="Rectangle_2" data-name="Rectangle 2" width="16" height="16" fill="#525254"></rect></clipPath></defs><g id="ico_Hektor" transform="translate(1862 1482)" opacity="0.3" clip-path="url(#clip-path-picto-hektor)"><g id="Groupe_4" data-name="Groupe 4" transform="translate(-1860 -1480)"><g id="Groupe_3" data-name="Groupe 3" transform="translate(0 0)" clip-path="url(#clip-path-picto-hektor-2)"><path id="Trac\xE9_2" data-name="Trac\xE9 2" d="M13.82,1.071Q12.046,0,8.019,0,4.064,0,2.4.961,0,2.289,0,5.8v4.434q0,3.474,2.4,4.8Q4.064,16,8.019,16a11.986,11.986,0,0,0,5.8-1.072Q16,13.635,16,10.235V5.8q0-3.436-2.18-4.73m.964,6.164a3.75,3.75,0,0,1-3.855,2.889A4.707,4.707,0,0,1,8,8.985a4.707,4.707,0,0,1-2.929,1.139A3.75,3.75,0,0,1,1.216,7.235a.059.059,0,0,1,.107-.049,2.291,2.291,0,0,0,2.022.957A7.284,7.284,0,0,0,6.45,6.8a.548.548,0,0,1,.46-.072L8,7.051l1.09-.319a.548.548,0,0,1,.46.072,7.283,7.283,0,0,0,3.105,1.34,2.292,2.292,0,0,0,2.022-.957.059.059,0,0,1,.106.049" transform="translate(0 0)" fill="#525254"></path></g></g></g></svg>
I want to flip SVG elements (path, line, polyline, text ...) without changing the original position. For example
<div id="canvas" style="width: 550px; height: 550px">
<svg version="1.2" baseProfile="tiny" id="Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 250 250" xml:space="preserve">
<text id="text" transform="matrix(1 0 0 1 31.1271 199.1222)" fill="#561010" font-family="'Roboto-Regular'" font-size="25.3945px">Nom d’entreprise</text>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="76.157" y1="87.0921" x2="70.9488" y2="44.8529"> <stop offset="0.0233" style="stop-color:#E92460"></stop> <stop offset="0.4636" style="stop-color:#F05C4B"></stop> <stop offset="0.9517" style="stop-color:#F36D38"></stop> </linearGradient>
<path transform="matrix(1 0 0 1 0 0)" fill="url(#SVGID_2_)" d="M51.75,55.77c-0.27-0.39,3.97-5.2,10.38-7.74c1.79-0.71,11.97-4.75,21.31,0.8
c8.61,5.12,11.99,15.71,10.53,24.27c-2.38,13.95-17.76,23.37-22.24,20.72c-0.97-0.57-1.32-2.75-1.99-7.09
c-1.81-11.84-1.68-20.54-1.68-20.54c0.05-3.05,0.19-4.38-0.61-6.25c-1.42-3.32-4.9-6.23-8.62-6.47
C54.84,53.21,51.97,56.08,51.75,55.77z"></path>
</g>
</svg>
</div>
I tried various solution such as changing transform to matrix(-1 0 0 1 -100 0). Also I have tried to add attribute transform-origin="center".
All I reached is a missplaced element. I have been trying the following:
flipHorizontal(){
let viewBox_array = this.getViewBox(); // this returns ['0', '0', '250', '250']
let canvas_width = $("#canvas").width();
this.selectedElements.forEach(element =>{
let element_width = element.getBoundingClientRect().width
element.setAttribute("transform-origin", "center")
var matrix = element.transform.baseVal[0].matrix;
matrix.a = (-1) * matrix.a
let diff = (element_width / (canvas_width / Number(viewBox_array[2])) )* Math.sign(matrix.a);
matrix.e += diff;
});
this.edit_done();
}
Also tried the following
flipHorizontal(){
this.selectedElements.forEach(element =>{
element.setAttribute("transform-origin", "center")
let bbox = element.getBBox();
var matrix = element.transform.baseVal[0].matrix;
matrix.a = (-1) * matrix.a
matrix.e += Math.sign(matrix.a) * (bbox.width + bbox.x);
});
this.edit_done();
}
As detailed in my answer here, the transform functions needed to flip in place are:
translate(<minX+maxX>,0) scale(-1, 1)
So, a working version of your code, might look something like this:
function flipHorizontal(selectedElements) {
selectedElements.forEach(element => {
// Get the bounds of the element
let bbox = element.getBBox();
// Get the current transform attribute (if any) of the element
let currentTransform = element.getAttribute("transform") || "";
// Append translate and scale functions to the transform that will flip the element in place
element.setAttribute("transform", currentTransform + ' translate('+(bbox.x + bbox.x + bbox.width) + ',0) scale(-1, 1)');
});
}
let elementsToFlip = document.querySelectorAll("path, text");
flipHorizontal( elementsToFlip );
<div id="canvas" style="width: 550px; height: 550px">
<svg version="1.2" baseProfile="tiny" id="Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 250 250" xml:space="preserve">
<text id="text" transform="matrix(1 0 0 1 31.1271 199.1222)" fill="#561010" font-family="'Roboto-Regular'" font-size="25.3945px">Nom d’entreprise</text>
<g>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="76.157" y1="87.0921" x2="70.9488" y2="44.8529"> <stop offset="0.0233" style="stop-color:#E92460"></stop> <stop offset="0.4636" style="stop-color:#F05C4B"></stop> <stop offset="0.9517" style="stop-color:#F36D38"></stop> </linearGradient>
<path transform="matrix(1 0 0 1 0 0)" fill="url(#SVGID_2_)" d="M51.75,55.77c-0.27-0.39,3.97-5.2,10.38-7.74c1.79-0.71,11.97-4.75,21.31,0.8
c8.61,5.12,11.99,15.71,10.53,24.27c-2.38,13.95-17.76,23.37-22.24,20.72c-0.97-0.57-1.32-2.75-1.99-7.09
c-1.81-11.84-1.68-20.54-1.68-20.54c0.05-3.05,0.19-4.38-0.61-6.25c-1.42-3.32-4.9-6.23-8.62-6.47
C54.84,53.21,51.97,56.08,51.75,55.77z"></path>
</g>
</svg>
</div>
You can do this with the only CSS, for example:
.logo_text--flipped, .logo_image--flipped {
transform-origin: center center;
transform: scaleX(-1);
}
<div id="canvas" style="width: 550px; height: 550px">
<svg version="1.2" baseProfile="tiny" id="Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 250 250" xml:space="preserve">
<g class="logo_text logo_text--flipped">
<text id="text" transform="matrix(1 0 0 1 31.1271 199.1222)" fill="#561010" font-family="'Roboto-Regular'" font-size="25.3945px">Nom d’entreprise</text>
</g>
<g class="logo_image logo_image--flipped">
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="76.157" y1="87.0921" x2="70.9488" y2="44.8529">
<stop offset="0.0233" style="stop-color:#E92460"></stop>
<stop offset="0.4636" style="stop-color:#F05C4B"></stop>
<stop offset="0.9517" style="stop-color:#F36D38"></stop>
</linearGradient>
<path transform="matrix(1 0 0 1 0 0)" fill="url(#SVGID_2_)" d="M51.75,55.77c-0.27-0.39,3.97-5.2,10.38-7.74c1.79-0.71,11.97-4.75,21.31,0.8
c8.61,5.12,11.99,15.71,10.53,24.27c-2.38,13.95-17.76,23.37-22.24,20.72c-0.97-0.57-1.32-2.75-1.99-7.09
c-1.81-11.84-1.68-20.54-1.68-20.54c0.05-3.05,0.19-4.38-0.61-6.25c-1.42-3.32-4.9-6.23-8.62-6.47
C54.84,53.21,51.97,56.08,51.75,55.77z"></path>
</g>
</svg>
</div>
So you can create a specific class and toggle it via JS
I am creating React components that will render out various SVGs:
const Close = ({
fill, width, height, float,
}) => (
<svg width={ `${width}px` } height={ `${height}px` } viewBox="0 0 14.48 14.48" style={ { float: `${float}`, cursor: 'pointer' } }>
<title>x</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Background">
<line style={ { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 } } x1="14.13" y1="0.35" x2="0.35" y2="14.13" />
<line style={ { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 } } x1="14.13" y1="14.13" x2="0.35" y2="0.35" />
</g>
</g>
</svg>
);
It's very convenient to be able to supply various attributed to this component to control dimensions, colour, etc...
What I don't have a good solution for, however, is handling the styles in a DRY manner. Note the line elements have the same value for style. I presently have them written inline because if I added an embedded stylesheet, then I would get class name collisions with other SVGs I render elsewhere on the page (our SVG software uses the same classes over and over).
<style scoped> has been removed from the spec: https://github.com/whatwg/html/issues/552
Shadow DOM is not yet supported by Edge: https://caniuse.com/#feat=shadowdomv1
Is there any other alternative for scoping styles?
To combine the best of both worlds, you could create an external styles file, as you would for CSS, but with exported style objects. You could then import it into any file that needs it.
As example, main file:
import React, { Component } from 'react';
import { render } from 'react-dom';
import * as Styles from './svgstyles';
class App extends Component {
render() {
return (
<div>
<svg width="100" height="200" viewBox="0 0 100 200">
<rect x="0" y="0" width="10" height="10" style={Styles.style1} />
<rect x="15" y="0" width="10" height="10" style={Styles.style2} />
<rect x="30" y="0" width="10" height="10" style={Styles.style3} />
<rect x="45" y="0" width="10" height="10" style={Styles.style4} />
<rect x="0" y="15" width="10" height="10" style={Styles.style4} />
<rect x="15" y="15" width="10" height="10" style={Styles.style3} />
<rect x="30" y="15" width="10" height="10" style={Styles.style2} />
<rect x="45" y="15" width="10" height="10" style={Styles.style1} />
</svg>
</div>
);
}
}
render(<App />, document.getElementById('root'));
And an external styles file:
export const style1 = {
stroke: 'red',
strokeWidth: "1",
fill: "blue",
}
export const style2 = {
stroke: 'red',
strokeWidth: "1",
fill: "green",
}
export const style3 = {
stroke: 'red',
strokeWidth: "1",
fill: "yellow",
}
export const style4 = {
...style3,
fill: "pink",
}
Live example here
If you just want to DRY up the code, you could create one style-object and reuse it:
const Close = ({
fill, width, height, float,
}) => {
const style = { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 }
return (
<svg width={ `${width}px` } height={ `${height}px` } viewBox="0 0 14.48 14.48" style={ { float: `${float}`, cursor: 'pointer' } }>
<title>x</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Background">
<line style={ style } x1="14.13" y1="0.35" x2="0.35" y2="14.13" />
<line style={ style } x1="14.13" y1="14.13" x2="0.35" y2="0.35" />
</g>
</g>
</svg>
);
}
This would also result in a small performance improvement since fewer objects would be created in each render cycle.
Actually, if I was in your place, Surely I use fonts instead of SVGs, but for your exact question, I prefer to use constant variables inside of my arrow function, just like below:
import React from 'react';
const Close = ({ fill, width, height, float }) => {
const constStyle = { fill: 'none', stroke: `${fill}`, strokeMiterlimit: 10 };
return (
<svg
width={`${width}px`}
height={`${height}px`}
viewBox="0 0 14.48 14.48"
style={{ float: `${float}`, cursor: 'pointer' }}
>
<title>x</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Background">
<line style={constStyle} x1="14.13" y1="0.35" x2="0.35" y2="14.13" />
<line style={constStyle} x1="14.13" y1="14.13" x2="0.35" y2="0.35" />
</g>
</g>
</svg>
);
};
export default Close;
Even, I make the line dimension variables as props, but I don't know what is your case exactly.
Hope this answer helps you.