September 3, 2021 / 15 min read
Last updated: October 19, 2023
In this post we are going to look into one way to allow embedded svg images to follow theme colors. We will be doing this in nextjs/react, using styled components and css variables.
To start off, I created a sample SVG image using Inkscape, and saved it as a plain svg.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="33.916588mm"
height="33.916588mm"
viewBox="0 0 33.916588 33.916588"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<g
id="layer1"
transform="translate(-67.532603,-100.81584)">
<circle
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path850"
cx="84.490898"
cy="117.77413"
r="15.958295" />
<circle
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
id="path1000"
cx="76.737106"
cy="110.88526"
r="2.1071048" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1500"
d="m 93.573987,123.14183 a 9.5406475,6.1189637 0 0 1 -9.215558,4.53526 9.5406475,6.1189637 0 0 1 -9.215558,-4.53526" />
<circle
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
id="circle1830"
cx="91.333298"
cy="110.88526"
r="2.1071048" />
</g>
</svg>
We can embed the SVG directly using the following code.
Which results in the following image.
The first idea you might get is to override the fill and stroke properties, so that we can override all the colors in the SVG. This is not possible because we're having a very specific problem here: we want to make the black colored lines responsive to the theme, but the red eyes can stay the same color in both themes. Even if we wanted to change the eye color depending on the theme, we couldn't use the same variable, since we still want a difference in color between the eyes and the rest of Albinie.
Did you know that there's an online tool that converts an svg element to jsx compatible code? If we use this tool with standard settings to convert our svg to jsx, we get the following code.
import React from "react";
function Icon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="128.189"
height="128.189"
version="1.1"
viewBox="0 0 33.917 33.917"
>
<g fillRule="evenodd" transform="translate(-67.533 -100.816)">
<circle
cx="84.491"
cy="117.774"
r="15.958"
fill="none"
stroke="#000"
strokeDasharray="none"
strokeMiterlimit="4"
strokeOpacity="1"
strokeWidth="2"
></circle>
<circle
cx="76.737"
cy="110.885"
r="2.107"
fill="red"
strokeWidth="0.265"
></circle>
<path
fill="none"
stroke="#000"
strokeDasharray="none"
strokeLinecap="round"
strokeMiterlimit="4"
strokeOpacity="1"
strokeWidth="2"
d="M93.574 123.142a9.54 6.119 0 01-9.216 4.535 9.54 6.119 0 01-9.215-4.535"
></path>
<circle
cx="91.333"
cy="110.885"
r="2.107"
fill="red"
strokeWidth="0.265"
></circle>
</g>
</svg>
);
}
export default Icon;
We can now use the svg as a react component.
Result:
Note that Albinie is not his original size any more. The image isn't scaled to the width of the blog, because the <img> tag isn't used here.
In order to automatically scale the svg, we can simply replace the width and height tags in the react component. Instead of defining a width in pixels, we set the width to 100% and remove the height tag altogether.
Result:
We continue by creating a styled component from the Icon component (this is the emoticon component). Here we will use theme data in order to set the right colors in the svg. In my case the colors are set in css variables, so we copy a color into the stroke property.
We also have to unpack the props from the styled component into the svg tag, so that the style property is correctly set.
function Icon(props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
version="1.1"
viewBox="0 0 33.917 33.917"
{...props}
>
}
Albinie just got some white in his eyes... And is still not responsive to theme changes. This is clearly not what we wanted to achieve. Do not despair however, as there is only one step left, editing the stroke props in the svg.
By removing the right stroke props from the svg, we can control which curves get their stroke set via the styled component, and which ones don't. In our case we want to make the mouth, and the circle around the face, responsive to theme changes. They are both colored black, so we can do a search and replace in the file.
ctrl + f, then search for stroke="#000" and replace all of them with an empty string. This results in the following.
Try changing the theme now with the button in the top right corner! An albino smiley will be clearly visible no matter what theme is chosen. Check out the robotics post if you would like to see theme-responsive svg illustrations that are actually useful.