Create SVG components with scoped CSS - css

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.

Related

how to create an Angular circular progress bar using SVG

I have created a design for angular circular progress bar. The issue is that i want to give the percentage value dynamic . I have got the dynamic value from API but i have no idea how should i implement it to create a circular progress bar using SVG. I'll share my html skeleton and the css i use.
.track,
.filled {
stroke-width: 10;
fill: none;
}
.track {
stroke: rgba(160, 215, 231, 0.85);
}
.filled {
stroke: blue;
stroke-dashoffset: 202;
stroke-dasharray: 440;
}
<div class="col-lg-3">
<div class="iconbox-singleD7 dashboard-height">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-35 0 190 190" class="mw">
<circle class="track" cx="60" cy="60" r="55" />
<text x="40" y="60" fill="#fff">Patients</text>
<text x="50" y="75" fill="#fff">{{totalPatient}}</text>
<circle class="filled" cx="60" cy="60" r="55" />
<circle cx="30" cy="136" r="5" stroke="blue" stroke-width="3" fill="blue" /><text x="40" y="140" fill="#fff">Male {{malePercentage}}%</text>
<circle cx="30" cy="152" r="5" stroke="rgba(160, 215, 231, 0.85)" stroke-width="3" fill="rgba(160, 215, 231, 0.85)" /><text x="40" y="155" fill="#fff">Female {{femalePercentage}}%</text>
</svg>
</div>
</div>
What i want is that. Lets say i get a male percentage = 70% or any value from an API i want to set the progress bar according to the percentage i get from API. How can i achieve that .i'm not familier with SVG. So any help will be appreciated. Thanks
Hello here is the code pen: https://codepen.io/Echyzen/pen/LYBraGB
var percentage = 75;
var a = -3.46;
var b = 440;
function changePer() {
const finalOffset = Math.round(a*percentage+b)
const concernedCircle = document.getElementsByClassName('filled')[0];
concernedCircle.style.strokeDashoffset = finalOffset;
}
which extract the a and b of the affine function.
You have to click on the button to execute the 75% percentage.
Or using querySelector:
var percentage = 70;
var a = -3.46;
var b = 440;
function changePer() {
const finalOffset = Math.round(a*percentage+b)
const concernedCircle = document.querySelector('.filled');
concernedCircle.style.strokeDashoffset = finalOffset;
}
Start with the <progress-circle> native Web Component I built : https://pie-meister.github.io/
It is unlicensed Open Source code; do with it whatever your want. Documentation at Dev.to
<progress-circle value="100%">Web Components</progress-circle>
<progress-circle value="71%" stroke="red">React</progress-circle>
<progress-circle value="90%" stroke="orange">Others</progress-circle>
<script src="https://pie-meister.github.io/PieMeister-with-Progress.min.js"></script>
<style>
progress-circle {
font-family: Arial;
width: 180px; /* StackOverflow viewport */
}
progress-circle::part(label) {
font-size:70px;
}
</style>

How to override a SVG color coming from backend?

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>

How do I target each circle within svg using css?

I have this
<svg class="wheel" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" transform="scale(1.022,1.022)" viewBox="0 0 1024 1024" height="100%" width="100%">
<circle fill="#ffffff" cx="512" cy="512" r="110"></circle>
<circle stroke="#ffffff" r="456" fill="transparent" stroke-width="33" cx="512" cy="512"></circle>
</svg>
How can I style each circle using css? Thank you.
If you know that the SVG markup won't change, then this will suffice:
.wheel * {
// CSS here
}
Otherwise, give the circles a class and write like:
.wheel .wheel-circle {
// CSS here
}
If you mean each circle needs different styles then simply:
.wheel #wheel-circle-1 {
// CSS here
}
.wheel #wheel-circle-2 {
// CSS here
}
Method 1:
The best way would be to add an id to each circle:
<svg class="wheel" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" transform="scale(1.022,1.022)" viewBox="0 0 1024 1024" height="100%" width="100%">
<circle id="innerCircle" fill="#ffffff" cx="512" cy="512" r="110"></circle>
<circle id="outerCircle" stroke="#ffffff" r="456" fill="transparent" stroke-width="33" cx="512" cy="512"></circle>
</svg>
And then just target that.
#innerCircle {
fill: red;
}
#outerCircle {
stroke: blue;
}
Method 2:
But if this is not desirable, try using the nth-child selector on the parent:
.wheel:nth-child(1) {
fill: red;
}
.wheel:nth-child(2) {
stroke: blue;
}
I hope this helps!

Create Magnifying glass for image with overlay react

I have a image and svg rect as overlay. when i try the react-image-magnifiyer and it working normally. but when i try to overlay with svg the magnifier didn't work. please guide me how to create magnifier on image hover with svg rect as overlay
JSX:
<div class="img-overlay-wrap" style={{ marginLeft: "27%" }}>
<img src={object.base64} style={{ height:`512` , width:`512`}} alt="" />
<svg style={{ display: (this.state.isHide ? 'block' : 'none') }} width="512" height="512" >
{
object.bbox.map((val,index)=>
<g>
<rect x={object.bbox[index][0]} y={object.bbox[index][1]} width={object.bbox[index][2]} height={object.bbox[index][3] }
style={{stroke:"red",paddingTop:"50px",strokeWidthTop:"20px"}} fill-opacity="0.0" />
</g>
)
}
</svg>
</div>
my attempt:
<div class="img-overlay-wrap" style={{ marginLeft: "27%" }}>
<GlassMagnifier className='img-overlay-wraps' imageSrc={object.name} />
<svg style={{ display: (this.state.isHide ? 'block' : 'none') }} width="512" height="512" >
{
object.bbox.map((val,index)=>
<g>
<rect x={object.bbox[index][0]} y={object.bbox[index][1]} width={object.bbox[index][2]} height={object.bbox[index][3] }
style={{stroke:"red",paddingTop:"50px",strokeWidthTop:"20px"}} fill-opacity="0.0" />
</g>
)
}
</svg>
</div>

How to wrap SVG content?

I want to use SVG in my WordPress site. The content of this SVG will be changed and I want it to be dynamic:
Here is my code:
<div style="height:60px;" class="gmr_main_page_svg_div">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="6773 1755 730 81" style="height:60px;" class="gmr_main_page_svg">
<defs>
<style>
.cls-1 {
clip-path: url(#clip-iPhone_6_7_8_1);
}
.cls-2 {
fill: #0652fd;
}
.cls-3, .cls-4 {
fill: none;
stroke: #0652fd;
}
.cls-3 {
stroke-width: 3px;
}
.cls-4 {
stroke-width: 4px;
}
.cls-5 {
fill: #fff;
font-size: 25px;
font-family: IRANSans-Bold, IRANSans;
font-weight: 700;
}
.cls-6 {
fill: rgba(255,255,255,0);
}
</style>
<clipPath id="clip-iPhone_6_7_8_1">
<rect x="6773" y="1755" width="900" height="81"/>
</clipPath>
</defs>
<g id="iPhone_6_7_8_1" data-name="iPhone 6/7/8 – 1" class="cls-1">
<rect class="cls-6" x="6773" y="1755" width="730" height="81"/>
<g id="Group_2830" data-name="Group 2830" transform="translate(6586 1751)">
<path id="Path_3672" data-name="Path 3672" class="cls-2" d="M1319.065,1810h189.586v-69.9h-131.61Z" transform="translate(-593 -1732)"/>
<path id="Path_3673" data-name="Path 3673" class="cls-3" d="M1319.065,1810h189.586v-69.9h-131.61Z" transform="translate(-600 -1736)"/>
<line id="Line_108" data-name="Line 108" class="cls-4" x1="728" transform="translate(187.5 76.5)"/>
</g>
<text id="خدمات_ما" data-name="خدمات ما" class="cls-5" transform="translate(7376 1779)"><tspan x="130.443" y="22">ویژگی‌های طراحی لوگو</tspan></text>
</g>
</svg>
</div>
Here is my a fiddle.
I used some image to SVG converters and this is the output of my image.
You can put the code on functions.php file,
Please check below code -
function cc_mime_types($mimes) {
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
add_filter('upload_mimes', 'cc_mime_types');
Try it!!

Resources