ICON OOP
Icons & Logos
Guide

How to change an icon's colour

Nine times out of ten, changing an SVG icon's colour is one CSS property. The tenth time it is impossible, and the difference between those two situations comes down to how the icon got onto the page.

HomeGuidesChange icon colour

There are two ways to change an icon's colour, and choosing between them is the whole job.

Set it before download. Use the tool, pick a colour, export. Right when you are handing the file to a designer, placing it in a document, or exporting a PNG, since a PNG's colour is permanent.

Set it in CSS. Download the SVG neutral and let your stylesheet drive it. Right for anything on the web, because one icon then handles every state and every theme.

The rule that explains most confusion

An SVG can only be recoloured with CSS if it is inline in your HTML. An SVG loaded through <img>, a CSS background-image, or an <object> is a separate document that your page styles cannot reach into. This one fact accounts for most "why won't my icon change colour" questions.

currentColor, the mechanism

currentColor is a CSS keyword meaning "whatever the computed color property is here". Icon sets ship with it in place of a hard-coded colour, which is what makes them themeable.

<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  <path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
.toolbar { color: #444; }   /* icon is #444 */
.toolbar a:hover { color: #FF5A36; }  /* icon follows, no icon CSS */

The important part is that color inherits. Set it on a container and every icon inside picks it up. Set it on a button and the icon matches the label automatically, which is exactly what you want and what hard-coded fills prevent.

fill or stroke: knowing which

Setting the wrong one is the single most common icon styling bug, and it produces two very recognisable failures.

Icon typeSetsColour withWrong property gives you
Stroke-basedLucide, Tabler, Featherstroke, with fill: noneA solid black blob
Fill-basedMaterial, Simple Icons, Phosphor fillfillNo visible change

How to tell them apart in two seconds: open the SVG and look at the root element. fill="none" stroke="currentColor" means stroke-based. fill="currentColor" with no stroke attribute means fill-based.

The global rule that breaks everything

svg { fill: currentColor; } in a stylesheet is a very common line and it turns every stroke-based icon on the page into a filled shape. If your Lucide icons suddenly render as solid blobs, search your CSS for this. Scope it to a class instead of applying it to every SVG.

Hover, focus, active, disabled

With currentColor, every state is a colour change on the parent and nothing else.

.icon-btn            { color: #6B7280; }
.icon-btn:hover      { color: #111827; }
.icon-btn:focus-visible { color: #FF5A36; outline: 2px solid currentColor; }
.icon-btn[disabled]  { color: #D1D5DB; }

Two things worth doing properly here. Use :focus-visible rather than :focus, so keyboard users get a clear indicator without mouse users getting an outline on every click. And do not rely on colour alone for disabled state, since a low-contrast icon is invisible to some users; pair it with aria-disabled and a cursor change. More in accessible icons.

Dark mode with zero extra files

Because icons inherit color, dark mode requires no icon work at all. Set your text colour per theme and the icons follow.

:root                    { --fg: #111; }
[data-theme="dark"]      { --fg: #EEE; }
body                     { color: var(--fg); }

One adjustment worth making: pure white icons on a dark background can look heavier than the same shape in black on white, an effect called irradiation. Using a slightly off-white, around #E8E8E8, often reads better than #FFFFFF. Material Symbols exposes a whole grade axis for exactly this problem.

CSS variables for icon colour

Once you have more than a handful of icon contexts, define the colours as tokens rather than repeating hex values.

:root {
  --icon-default: #6B7280;
  --icon-active:  #FF5A36;
  --icon-danger:  #DC2626;
  --icon-muted:   #D1D5DB;
}
.icon         { color: var(--icon-default); }
.icon--active { color: var(--icon-active); }
.icon--danger { color: var(--icon-danger); }

The payoff is that rebranding, adding a theme or adjusting contrast for accessibility becomes a change in one block rather than a search across a codebase. It also gives your design and engineering teams shared vocabulary, which is most of what a design system is.

When the SVG is in an img tag

You cannot recolour it. The SVG is a separate document and CSS does not cross that boundary. Four ways out, in order of preference:

  1. Inline it instead. The correct fix in almost every case.
  2. Use an SVG sprite with <use>. Keeps one shared file while remaining stylable, as long as the sprite's shapes use currentColor.
  3. Use CSS mask-image. The SVG becomes a mask and background-color becomes the icon colour. Works for single-colour icons and is well supported now.
  4. Export a second file in the colour you need. Crude, but perfectly reasonable for one or two static icons.

The mask technique, since it is less known:

.icon-mask {
  width: 24px; height: 24px;
  background-color: currentColor;
  -webkit-mask: url(icon.svg) center / contain no-repeat;
          mask: url(icon.svg) center / contain no-repeat;
}

Note the CSS filter approach you will find in older answers, chaining invert, sepia, saturate and hue-rotate to approximate a colour. It is imprecise, unreadable, and there is no reason to use it now that masks are supported.

Multi-colour icons

currentColor gives you one colour. For two or more, either target the shapes individually, which requires inline SVG and shapes you can address, or use CSS variables inside the SVG itself:

<svg viewBox="0 0 24 24">
  <path d="..." fill="var(--icon-primary, #FF5A36)"/>
  <path d="..." fill="var(--icon-secondary, #FFD9CF)"/>
</svg>

Now the page controls both colours through variables, with sensible fallbacks if they are not set. This is a clean pattern for duotone icons, and it is roughly how Phosphor's duotone weight is intended to be handled.

Brand logos are the exception: do not recolour a full-colour brand mark to fit your palette, since most brand guidelines prohibit it. Use the monochrome version of that mark instead. See brand logos.

Troubleshooting

SymptomCauseFix
Nothing changesSVG is in an img tag or backgroundInline it, or use mask-image
Icon is a solid blobfill set on a stroke iconSet fill: none, colour the stroke
Only part changesSome shapes have hard-coded fillsReplace those with currentColor
Colour ignoredInline attribute beating your CSSRemove the attribute, or raise specificity
Works locally, not in productionAn SVG optimiser inlined the coloursConfigure it to preserve currentColor

Frequently asked questions

If the SVG is inline in your HTML and uses currentColor, set the CSS color property on the icon or any parent element and the icon follows. If it is stroke-based, colour the stroke and keep fill set to none; if it is fill-based, colour the fill. If the SVG is loaded through an img tag it cannot be recoloured with CSS at all.
Most likely the SVG is loaded through an img tag, a CSS background image or an object element, all of which create a separate document your page styles cannot reach into. Inline the SVG instead, use an SVG sprite with the use element, or apply the icon as a CSS mask-image with background-color providing the colour.
It is a CSS keyword meaning the computed value of the color property at that point. Icon sets ship with fill or stroke set to currentColor instead of a fixed value, so the icon inherits colour from its context. Because color inherits down the tree, setting it on a button or container colours every icon inside with no icon-specific CSS.
It depends on how the icon is drawn. Stroke-based sets such as Lucide, Tabler and Feather need stroke coloured with fill kept at none; setting fill turns them into solid blobs. Fill-based sets such as Material Symbols and Simple Icons need fill; setting stroke does nothing visible. Open the SVG and check the root element to see which it is.
Set your foreground colour per theme with a CSS variable and let icons inherit it through currentColor. No extra icon files or swapping logic are needed. One refinement: pure white icons on dark backgrounds can look slightly heavy, so a marginally off-white value often reads better than pure white.
Yes, if the SVG is inline. Give each shape its own fill referencing a CSS variable with a fallback, then control both from your stylesheet. This is the cleanest way to handle duotone icons. Do not use this to recolour full-colour brand logos, since altering official marks breaches most brand guidelines.

Recolour an icon right now

Pick any icon, set the exact colour and stroke, and export SVG, PNG or ready-to-paste markup.

Open the ICON OOP tool