How to svg theming: tailwind fill-current vs. css custom properties
- Step 1Count the colours that must move independently — If the icon is one colour, or all colours should always change together, the Tailwind
currentColormodel from svg-to-tailwind is enough. If primary and accent must theme separately, you need the variable model. - Step 2Decide whether Tailwind is a hard dependency —
fill-currentis Tailwind-specific. CSS custom properties work in any project, Tailwind or not. For an icon set shipped to mixed consumers, the variable approach is more portable. - Step 3Produce the Tailwind form — Run the icon through svg-to-tailwind. It rewrites every colour to
currentColorand addsfill-currentto the root<svg>. Wrap it intext-*/dark:text-*to drive colour. - Step 4Produce the variable form — Run the icon through svg-css-variable-injector. With no map it auto-assigns
--svg-color-1,--svg-color-2, … to each distinct colour; with a map you name them (e.g.#1a1a2e:--icon-primary). - Step 5Set defaults that degrade gracefully — For the variable form, write
fill="var(--icon-primary, currentColor)"so it still works when the variable is undefined — giving you a hybrid that falls back to the Tailwind behaviour. - Step 6Standardise across the library — Pick one scheme for the whole set. Consumers shouldn't have to remember that some icons take
text-*and others take--icon-*. Document the chosen contract once.
Tailwind currentColor vs. CSS custom properties
The two schemes side by side. The JAD tool for each is linked. The website svg-to-tailwind runs only the single-colour current mode.
| Dimension | Tailwind / currentColor | CSS custom properties |
|---|---|---|
| Produced by | svg-to-tailwind | svg-css-variable-injector |
| Output markup | fill="currentColor" + class="fill-current" on root | fill="var(--svg-color-1)" (auto) or named vars |
| Colours addressable | One (all shapes share currentColor) | Each distinct colour independently |
| Multi-colour icons | Collapse to one colour | Each colour themed separately |
| How you set colour | text-blue-500 on a parent | --icon-primary: #… in CSS |
| Dark mode | dark:text-white on parent — trivial | A separate --icon-* override block |
| Needs Tailwind? | Yes (the utility classes) | No — works in any project |
| Runtime cost | Zero (pure CSS) | Zero (pure CSS) |
Embedding model: how each survives different SVG uses
Both schemes rely on CSS inheritance, so neither themes when the SVG is loaded as an external image.
| Embedding | currentColor (Tailwind) | CSS variables |
|---|---|---|
Inline <svg> in HTML/JSX | Themeable (inherits color) | Themeable (reads document vars) |
<img src="icon.svg"> | Not themeable — no parent color to inherit | Not themeable — can't read document vars |
background-image: url(...) | Not themeable | Not themeable |
<use href="sprite#id"> (same doc) | Themeable (currentColor crosses <use>) | Vars must be defined where <use> renders |
Cookbook
The same icon expressed both ways, plus the hybrid fallback that bridges them.
Tailwind form (one shared colour)
svg-to-tailwind output. Both shapes share currentColor, driven by the parent text-* class. Simple, dark-mode-ready, monochrome.
<span class="text-slate-700 dark:text-white">
<svg class="fill-current" viewBox="0 0 24 24">
<path d="..." fill="currentColor"/>
<circle cx="18" cy="6" r="3" fill="currentColor"/>
</svg>
</span>
→ primary and accent are the SAME colour.CSS variable form (two independent colours)
svg-css-variable-injector output with a named map. Primary and accent are separate variables, so each themes independently.
<style>
.icon { --icon-primary: #1a1a2e; --icon-accent: #4361ee; }
.dark .icon { --icon-primary: #fff; --icon-accent: #93c5fd; }
</style>
<svg class="icon" viewBox="0 0 24 24">
<path d="..." fill="var(--icon-primary)"/>
<circle cx="18" cy="6" r="3" fill="var(--icon-accent)"/>
</svg>Hybrid: variable with a currentColor fallback
Use the variable when defined, fall back to inherited color otherwise. The icon then behaves like the Tailwind form in projects that don't set the variable.
<path d="..." fill="var(--icon-primary, currentColor)"/> No --icon-primary set → follows parent text-* (Tailwind-style) --icon-primary set → uses the explicit variable
Why img embedding breaks both
An external SVG has no parent element in the host document, so neither currentColor nor document variables resolve. Inline to theme.
<img src="icon.svg" class="text-blue-500"> <!-- ignored -->
currentColor → resolves inside the SVG's own root color
(usually black), NOT the page
var(--x) → undefined inside the isolated image doc
Fix: inline the <svg> element directly in the page.Choosing for an npm icon library
For a package consumed by both Tailwind and non-Tailwind apps, the hybrid var(--icon-color, currentColor) is the most portable single contract.
Library default per shape:
fill="var(--icon-color, currentColor)"
Tailwind consumer: text-blue-500 on wrapper → works
Plain CSS consumer: .icon { --icon-color: red } → works
Neither set: inherits color (black) → worksEdge cases and what actually happens
Tailwind tool can't keep two colours apart
By designThe website svg-to-tailwind runs only current mode, which maps every colour to one currentColor. If you need primary and accent to theme independently, that's the signal to choose CSS variables (or the API-only palette mode), not the default Tailwind conversion.
CSS variables ignored when SVG is an `<img>`
Not supportedAn SVG referenced via <img src> or background-image is an isolated document — it cannot read the host page's custom properties. This affects the variable scheme exactly as currentColor non-inheritance affects the Tailwind scheme. Only inline SVG is themeable.
currentColor inside `<img>` resolves to the SVG's own color
Watch closelycurrentColor in an external SVG doesn't disappear — it resolves to the color set inside the SVG's own root (usually the initial value, black). So the icon renders, but not in your page's theme colour. People mistake this for the tool 'not working'; it's the embedding model.
Dark mode needs different wiring per scheme
ExpectedTailwind form: add dark:text-white — done. Variable form: you must add a .dark .icon { --icon-*: … } override block. Neither is automatic across schemes, so document the dark-mode contract for whichever you pick.
Mixing both schemes in one set
Watch closelyIf some icons take text-* and others take --icon-*, consumers can't apply colour uniformly and bugs follow. Pick one scheme per library. The hybrid var(--x, currentColor) is the only clean way to support both at once.
Variable names collide with app variables
Watch closelyAuto-assigned --svg-color-1/-2 are generic and can clash if reused across icons inlined in the same scope. Use a named map in svg-css-variable-injector (e.g. --brand-icon-primary) to namespace them.
Gradients themeable in neither scheme directly
Not supportedcurrentColor can't express a gradient, and a gradient's stops aren't fill/stroke so the Tailwind tool won't touch them. With variables you can put var(--x) on a stop-color, but that's manual. For gradient recolouring, reach for svg-hex-swapper.
Tailwind purges the utility classes
Watch closelyfill-current and dynamically-applied text-* can be tree-shaken if not present in scanned source. The variable scheme has no such purge risk (it's plain CSS). Factor build-purge fragility into the choice for dynamically-coloured icons.
Zero runtime in both — performance is a wash
ExpectedBoth schemes are pure CSS with no JS at render time, so neither has a runtime cost advantage. The decision is about colour independence, portability, and dark-mode ergonomics — not speed.
Frequently asked questions
When should I pick fill-current over CSS variables?
Pick fill-current when the icon is monochrome (or all colours should change together) and your project already uses Tailwind. It's the simplest path, dark mode is dark:text-*, and there's nothing to manage. Use svg-to-tailwind to produce it.
When are CSS variables the better choice?
When you need independent control of multiple colours in one icon, or when the icon set ships to non-Tailwind consumers. Variables are framework-agnostic and per-colour. Produce them with svg-css-variable-injector.
Can the Tailwind tool give each colour its own class?
Not from the website — it runs the single-colour current mode there. The palette mode does assign a per-colour Tailwind class, but it's only available via the API/runner. For per-colour control inside the browser, CSS variables are the practical route.
Can I use both in the same SVG?
Technically yes, but it's confusing for consumers. The clean middle ground is the hybrid fill="var(--icon-color, currentColor)": it reads the variable when set and falls back to the inherited color (Tailwind behaviour) otherwise.
Which works better with Tailwind dark mode?
currentColor/fill-current — just add dark:text-white to the parent and the icon repaints. CSS variables need an explicit .dark override block to change --icon-*. So for pure dark-mode ergonomics the Tailwind scheme wins.
Do CSS variables work in an SVG used as an img?
No. An SVG loaded via <img src> or background-image is an isolated document and cannot read the host page's custom properties. Inline the SVG to make either scheme themeable.
Does currentColor work when the SVG is an img?
It doesn't break, but it resolves to the color set inside the SVG's own root (usually black), not your page colour — so the icon won't follow your theme. Same root cause as the variable limitation: external SVGs can't inherit from the host page.
Which is more portable for an npm icon package?
CSS variables, and specifically the hybrid var(--icon-color, currentColor). It works in Tailwind apps (via currentColor fallback), in plain-CSS apps (set the variable), and even with nothing configured (inherits color).
Is there a performance difference?
No meaningful one. Both are pure CSS, evaluated by the browser at paint time with no JavaScript. Choose on colour independence and portability, not speed.
How do I theme a gradient with either approach?
Neither approach themes gradients automatically — currentColor can't represent a gradient and the Tailwind tool ignores stop-color. Recolour gradient stops with svg-hex-swapper, or hand-bind stop-color="var(--x)" if you're committed to the variable scheme.
What if I want a single solid brand colour, not the parent's colour?
That's not what currentColor is for — it always inherits. Either keep that hex fixed (e.g. with svg-hex-swapper) or assign it a named CSS variable you set once via svg-css-variable-injector.
Can I convert a variable-based icon to the Tailwind form later?
Running an icon that uses fill="var(--x)" through svg-to-tailwind's current mode would overwrite the variable with currentColor (the value isn't none), discarding the per-colour control. Decide the scheme before committing the library; converting one way is lossy.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.