How to Recolor SVG Icons with CSS (and Why It Sometimes Fails)
Most single-color icons are a handful of paths sharing one fill. If you can control that fill from CSS, one icon file can serve your light theme, your dark theme, your brand color and your hover states. When it does not work, the cause is almost always one of a few well-known gotchas. This guide covers the techniques that work and how to diagnose the ones that do not.
Use currentColor so icons follow text
The keyword currentColor means the element's current text color. If an icon's fill is set to currentColor, it automatically matches the surrounding text, including any color changes you make on hover or in dark mode. This is usually the best default for interface icons.
.button svg {
fill: currentColor;
}
.button {
color: #e4e4e7;
transition: color 0.2s;
}
.button:hover {
color: #1793d1;
}Drive the color with a custom property
When an icon needs a color unrelated to the nearby text, such as a brand color, expose a CSS variable and read it in the fill. The fallback value after the comma is used when the variable is not set, so the icon still renders sensibly on its own.
svg.brand {
fill: var(--icon-color, #888);
}
@media (prefers-color-scheme: dark) {
:root {
--icon-color: #e4e4e7;
}
}Why your color change is ignored
The paths have their own fill attribute
If you set fill on the svg element but each path carries fill="#000" in the file, the path's own value wins over the inherited one and nothing seems to happen. Either remove the fill attributes from the file, or target the paths directly with a selector such as svg path.
The icon uses strokes, not fills
Outline-style icons are drawn with stroke rather than fill, so changing fill has no visible effect. Set stroke: currentColor instead, and keep fill: none so the shapes stay hollow.
The SVG is loaded from a file
CSS on your page cannot reach inside an SVG loaded through an img tag or a CSS background, because it is rendered as a separate document. Your options are to inline the markup, to use the file as a CSS mask and set background-color, or to change the fill in the file itself before serving it. The download button on each icon page here does the last one: it writes your chosen hex color into the fill attribute of the SVG.
Animating the change
Fill and stroke can be transitioned like any other color property. Add a short transition so hover and theme changes feel smooth rather than abrupt.
svg path {
fill: #a1a1aa;
transition: fill 0.2s ease;
}
svg:hover path {
fill: #1793d1;
}Theming with a class or data attribute
The prefers-color-scheme media query follows the visitor's system setting. If your site also has a manual theme toggle, define the variable under a class or data attribute on the root element as well, so the toggle can override the system preference. Every icon that reads the variable updates at once, with no JavaScript touching the SVG itself.
:root[data-theme="light"] {
--icon-color: #27272a;
}
:root[data-theme="dark"] {
--icon-color: #e4e4e7;
}You may also see CSS filter tricks suggested for recoloring images. They can approximate a color, but the result is hard to predict and impossible to match to an exact hex value, so prefer masks or inline SVG whenever the color matters.
A note on multi-color logos
Logos with several colors usually have a different fill per path, and those colors are part of the brand. Recoloring them with one rule flattens the design and can break trademark guidelines. Treat those as fixed artwork and recolor only single-color marks, ideally to the colors the project's own brand guidelines allow.
Quick checklist
- Is the SVG inline, or loaded from a file? Files need a mask or an edited fill.
- Do the paths hard-code a fill? Remove it or override it per path.
- Is it a stroke icon? Change stroke, not fill.
- Want it to follow text? Use currentColor.