:root {
--logo-color: red;
--text-color: blue;
}
* {
color: var(--text-color)
}
.logo{
color: var(--logo-color);
}
<svg
class="logo"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="5" r="3" />
<line x1="12" y1="22" x2="12" y2="8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
</svg>
I attempted to modify the color of an SVG using the color
property in my CSS file. I selected the .logo
class and anticipated the logo to appear in red. However, it ended up being displayed in blue, as if the generic CSS (0-0-0) had higher precedence than the class-specific styles (0-1-0). And if I remove the color
property from the generic block, then it works normally.
*
)The culprit in your code is that even though *
has the weakest specificity, it actually matches everything, not only the svg
element (that by itself does not "draw" anything), but also all its descendants (that do the actual drawing). So basically your explicit
svg { color: red; }
has no effect, because its children (circle
, line
and path
) elements are all then again matched by the preceding
* { color: blue; }
so the color: red
that would otherwise be inherited from the parent is there overridden to blue
from *
. In a simplified sample:
* {
color: blue;
}
svg {
color: red;
}
html { color-scheme: light dark; }
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="5" r="3" />
<line x1="12" y1="22" x2="12" y2="8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
</svg>
There are several ways how to cope with this. One could be overriding general child selector in the more specific selector: svg *
(.logo *
) into your CSS
* {
color: blue;
}
svg * {
color: red;
}
html { color-scheme: light dark; }
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="5" r="3" />
<line x1="12" y1="22" x2="12" y2="8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
</svg>
Fact that CSS color
property affects your strokes is indeed set by SVG's root node stroke="currentColor"
attribute, that makes SVG and all its children copy the computed color
value into stroke
(unless explicitly told otherwise). It is usually a good practice to do this and not copy colour values to stroke
and/or fill
.
Best practice would probably be not using *
at all and let implicit color
value inheritance do its work:
html {
background-color: darkslategray;
color: tan;
}
.red {
color: red;
}
/*
Just for brevity.
It is always better to keep SVG presentational attributes in the document
rather than in CSS; *especially* dimensions and fills/strokes.
*/
svg {
width: 24px;
height: 24px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
vertical-align: text-bottom;
}
<p>Text with
<svg viewBox="0 0 24 24" id="anchor">
<circle cx="12" cy="5" r="3" />
<line x1="12" y1="22" x2="12" y2="8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
</svg>
normal SVG.
<p>Red SVG:
<svg class="red" viewBox="0 0 24 24">
<use href="#anchor" />
</svg>
and normal text again.
<p class="red">
Red text with
<svg viewBox="0 0 24 24">
<use href="#anchor" />
</svg>
red SVG.
Universal selector *
is rarely optimal solution for any task. Besides introducing potential "footguns" like this one, it can even be slightly detrimental for performance.