Edit: In my case, I need IE11 support, so CSS variables aren't the way to go. If you can use CSS variables, see Kaiido's answer. If not, see Paul's answer.
I've got a simple website with the concept of "light" and "dark" themes. Light themes are white or very light backgrounds, and dark themes are some darker color. Most components on the website work by adjusting their color based on their position inside elements that have these themes. For example:
.dark-theme {
background-color: black;
}
.light-theme {
background-color: white;
}
/* When the paragraph is inside a dark theme */
.dark-theme p,
.light-theme .dark-theme p {
color: white;
}
/* When the paragraph is inside a light theme */
.light-theme p,
.dark-theme .light-theme p {
color: black;
}
.dark-theme, .light-theme {
padding: 10px;
}
<div class="light-theme">
<p>Light Theme</p>
<div class="dark-theme">
<p>Dark Theme</p>
</div>
</div>
<div class="dark-theme">
<p>Dark Theme</p>
<div class="light-theme">
<p>Light Theme</p>
</div>
</div>
This allows us to have each component just define how it needs to display in dark vs. light theme, and its placement in the DOM will take care of the rest.
The catch here is that we must actually write definitions for each case (dark on light, and light on dark). If we don't define one case, then we will get incorrect behaviour when the elements are nested.
However, I cannot get this to work with SVGs in a specific scenario where the SVG has a stroke defined as an HTML attribute. We are using icons that come from somewhere else so adjusting the SVG markup is not an option.
What we want is: when an SVG is on a light background (.light-theme), the SVG should be whatever color is in the SVG markup (there may be more than one). Whenever an SVG is on a dark background (.dark-theme), we want it to be white (working).
Below is what happens when we try the same thing as we did above with the <p> using the stroke or fill properties of the SVG. It seems I cannot use unset, inherit, or initial to get it to fall back to the SVG's actual value. Is this expected?
And if I just don't define the behaviour for the light theme, it will fail in cases where the themes are nested.
So, the tl;dr is: how can I override stroke and fill properties that were set using CSS with a more specific CSS rule to get an SVG to use its original color that was defined in its markup?
/* When the paragraph is inside a dark theme */
.dark-theme p,
.light-theme .dark-theme p {
color: white;
}
/* When the paragraph is inside a light theme */
.light-theme p,
.dark-theme .light-theme p {
color: black;
}
/* When the circle is inside a dark theme */
.dark-theme svg circle,
.light-theme .dark-theme svg circle {
stroke: white;
fill: white;
}
/* When the circle is inside a light theme */
.light-theme svg circle,
.dark-theme .light-theme svg circle {
stroke: initial; /* How to make the circle red here? */
fill: initial; /* How to make the circle red here? */
}
/** helpers below here **/
svg {
display: block;
margin: 0 auto;
}
.light-theme, .dark-theme {
padding: 10px;
}
.dark-theme {
background-color: black;
}
.light-theme {
background-color: white;
}
<div class="light-theme">
<p>Light Theme</p>
<div class="dark-theme">
<p>Dark Theme</p>
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="red" stroke-width="3" fill="red" />
</svg>
</div>
</div>
<div class="dark-theme">
<p>Dark Theme</p>
<div class="light-theme">
<p>Light Theme</p>
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="red" stroke-width="3" fill="red" />
</svg>
</div>
</div>
By request: here's an example of how it could be nested. There are essentially no DOM constraints we can rely on.
<div class="light-theme"> <!-- nesting can go light > dark > light > dark, etc -->
<div class="dark-theme">
<p>Dark Theme</p>
<div class="something anything really"> <!-- this may or may not be here -->
<div class="another container dark-theme"> <!-- this may or may not be here -->
<div class="light-theme">
<p>Light Theme</p>
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="red" stroke-width="3" fill="red" />
</svg>
</div>
</div>
</div>
</div>
</div>