How to replace hardcoded svg colors with css custom properties
- Step 1Open the tool and drop your SVG — Drag an
.svgonto the CSS Variable Injector above (accepted input is.svg/image/svg+xml). It loads in the browser; nothing uploads. This tool requires a Pro plan (itsminTierispro). - Step 2Decide: explicit mapping or auto-assign — For named tokens, click + Add mapping to create a row. For a quick blanket parameterisation, leave the map empty — at process time the tool detects every
#hexand assigns--svg-color-Nautomatically. - Step 3Build each mapping row — Each row is a native colour picker on the left (which always produces a 6-digit hex like
#003082) → an arrow → a text field for the variable name (placeholder--brand-primary). If you type a name without the leading--, the tool prepends it for you. Remove deletes a row. - Step 4Match the picker to a colour that actually exists in the file — The picker emits a 6-digit hex; the SVG must contain that exact 6-digit string for the replacement to land. If your source uses 3-digit shorthand (
#03f) the browser tool won't expand it for you — see the edge cases. Open the SVG in a text editor first to confirm the literal hex values. - Step 5Process and download the rewritten SVG — The output is one file,
<name>-css-vars.svg, with each mapped colour replaced byvar(--your-name)(orvar(--svg-color-N)in auto mode). The tool does not also hand you a CSS file — you author the:rootdeclarations. - Step 6Add the SVG inline and declare the variables — Paste the SVG markup directly into your HTML (not as
<img src>), then add:root { --brand-primary: #003082; }to your stylesheet. Use a fallback in the SVG output where it matters, e.g.var(--brand-primary, #003082), so the icon still renders if the variable is undefined.
The one option this tool exposes
The injector has a single option, variableMap. Everything else is automatic. Values shown are the real defaults from the schema.
| Option | Type | Default | What it does |
|---|---|---|---|
variableMap | list of #hex:--varName pairs (UI: colour picker → name field rows) | [] (empty) | When you add rows, only those colours are mapped to the names you give. When empty, the tool auto-detects every #hex and assigns sequential --svg-color-N. |
| (auto mode trigger) | n/a — implicit when variableMap is empty | — | Auto-assignment runs only if the map is empty. Adding even one explicit row turns auto-detect OFF, so unmapped colours stay hardcoded. |
What gets parameterised vs. what stays hardcoded
The injector does a literal, case-insensitive string replace of each target hex. This table shows what that does and does not catch.
| Colour form in the SVG | Auto-detect? | Explicit map? | Notes |
|---|---|---|---|
fill="#003082" (6-digit hex) | Yes | Yes | The primary case. Replaced wherever the literal string appears. |
stop-color, flood-color, stroke | Yes | Yes | These are plain attribute values containing the hex, so the literal replace covers gradient stops and filter floods. |
Hex inside style="fill:#003082" | Yes | Yes | The replace is text-level, so hexes embedded in inline style are matched too. |
3-digit shorthand #03f | Yes (matched as #03f) | Browser: only if you type #03f; engine expands #03f→#0033ff | Auto-detect maps #03f to its own --svg-color-N separately from #0033ff. See edge cases for the browser/engine difference. |
Named colour fill="red" | No | No (picker can't emit names) | The picker only produces hex, and auto-detect only scans #hex. Convert names to hex first with svg-hex-swapper. |
rgb(0,48,130) / hsl(...) | No | No | Functional colour notations are not detected or mapped. Normalise to hex upstream. |
Cookbook
Real before/after fragments. The tool outputs the SVG only — the :root block in each example is the CSS you write yourself.
Explicit mapping of a two-colour logo
Map the navy and red to named brand tokens. Every literal occurrence of each hex is rewritten, including inside a gradient stop.
Mappings:
#003082 → --brand-primary
#e02d3c → --brand-accent
Before:
<svg viewBox="0 0 24 24">
<path fill="#003082" d="..."/>
<rect stroke="#e02d3c" .../>
<stop offset="1" stop-color="#003082"/>
</svg>
After (downloaded as logo-css-vars.svg):
<svg viewBox="0 0 24 24">
<path fill="var(--brand-primary)" d="..."/>
<rect stroke="var(--brand-accent)" .../>
<stop offset="1" stop-color="var(--brand-primary)"/>
</svg>
CSS you add yourself:
:root { --brand-primary:#003082; --brand-accent:#e02d3c; }Auto-assign on an unfamiliar icon
Leave the map empty. The tool walks the SVG, collects each distinct #hex in first-appearance order, and names them sequentially.
variableMap: [] (empty → auto mode)
Before:
<path fill="#1a1a2e" .../>
<path fill="#16213e" .../>
<path fill="#1a1a2e" .../> <-- repeat of the first colour
After:
<path fill="var(--svg-color-1)" .../>
<path fill="var(--svg-color-2)" .../>
<path fill="var(--svg-color-1)" .../> <-- same variable, deduped by value
CSS you add:
:root { --svg-color-1:#1a1a2e; --svg-color-2:#16213e; }Add a fallback so the icon never renders blank
The tool emits bare var(--name). To make the SVG robust when the variable is undefined, edit the output (or your inline copy) to add a fallback value.
Tool output: <path fill="var(--icon-color)" .../> Hand-edit to add a fallback: <path fill="var(--icon-color, #1a1a2e)" .../> Now the path falls back to #1a1a2e if --icon-color is never declared (e.g. SVG dropped into a page that hasn't loaded the theme stylesheet yet).
Parameterise only some colours, keep others fixed
Map just the themeable UI colour; leave a fixed brand colour hardcoded. Because adding one row disables auto-detect, the unmapped colour stays exactly as-is.
Mapping (one row only): #1a1a2e → --icon-color Before: <path fill="#1a1a2e" .../> (UI colour — themeable) <path fill="#e8b923" .../> (gold trademark — must stay fixed) After: <path fill="var(--icon-color)" .../> <path fill="#e8b923" .../> <-- untouched (auto-detect was OFF)
Inline the result so the cascade reaches it
CSS variables only flow into an SVG that is inlined in the document. The same file used via <img src> will not pick up :root variables.
Works (inline):
<body>
<style>:root{--icon-color:#1a1a2e}</style>
<svg viewBox="0 0 24 24">
<path fill="var(--icon-color)" d="..."/>
</svg>
</body>
Does NOT work (external reference):
<img src="icon-css-vars.svg"> <-- var() resolves to nothing
background-image:url(icon-css-vars.svg) <-- same problemEdge cases and what actually happens
Output is the SVG only — no CSS file is generated
By designThe tool returns a single file, <name>-css-vars.svg, with colours rewritten to var(--name). It does not also emit a :root { … } declarations file. You author the CSS yourself (each cookbook example shows the matching block). This is the most common expectation mismatch, so plan to write the declarations.
SVG used as `<img src>` or `background-image` shows no theming
ExpectedCSS custom properties only cascade into SVG that is inlined directly in the HTML. An externally-referenced SVG (<img>, background-image, <object> in most cases) is an isolated document and never sees your page's :root variables. Inline the markup for theming to take effect.
Picker emits 6-digit hex but the SVG uses 3-digit shorthand
May not match (browser)The colour picker always produces a 6-digit hex (e.g. #0033ff). In the browser tool, an explicit mapping is matched literally and the document is not pre-expanded, so a file written as #03f will not be hit by a #0033ff mapping. The server-safe engine expands your supplied colour (#03f→#0033ff) but still matches against the raw document text. Safest path: normalise the SVG to 6-digit hex first (e.g. via svg-hex-swapper).
Named colours (`red`, `currentColor`) are ignored
Not detectedAuto-detect scans only #hex patterns, and the picker can only emit hex — so fill="red", stroke="currentColor", and CSS keyword colours are never parameterised. Convert keyword colours to hex first, or use svg-to-tailwind if currentColor is actually what you want.
`rgb()` / `hsl()` values are skipped
Not detectedFunctional colour notations are not part of the #hex scan and the picker can't produce them, so fill="rgb(0,48,130)" passes through unchanged. Normalise to hex upstream if you need those colours themeable.
Adding one explicit row turns auto-detect off
By designAuto-assignment runs only when variableMap is empty. The moment you add a single mapping row, the tool maps exactly those colours and leaves every other colour hardcoded. That is the intended way to parameterise some colours and freeze others — but it surprises people who add one row and expect the rest to auto-fill.
Two near-identical hexes both get mapped
ExpectedReplacement is value-based, so #1a1a2e and #1b1b2f are different colours and (in auto mode) get different --svg-color-N variables even though they look alike. If a designer used several almost-equal greys, you'll get several variables — consolidate them upstream with svg-hex-swapper first if you want one token.
Free plan blocks the tool
402 upgrade requiredThis tool's minimum tier is pro. SVG tier limits also apply to the file size: Free 5 MB, Pro 50 MB, Pro+Media 200 MB, Developer 2 GB. Icon-scale SVGs are kilobytes, so size is rarely the constraint — the tier gate is.
Frequently asked questions
Does the tool also give me the CSS variable declarations?
No. It outputs one file — the rewritten SVG (*-css-vars.svg) — where colours have become var(--name). You write the :root { --name: #hex; } block yourself. Each cookbook example shows the exact CSS to pair with the output. The tool does not emit a .css file.
How do CSS variables actually work inside an SVG?
When the SVG is inlined in your HTML, it lives in the same document and inherits the page's CSS custom properties. A fill="var(--brand-primary)" resolves against the cascade, so declaring :root { --brand-primary: #003082; } recolours every matching shape instantly. The mechanism is ordinary CSS inheritance applied to SVG presentation properties.
Why doesn't it work when I use the SVG as an `<img>`?
An SVG referenced via <img src> or background-image is loaded as an independent document and is sandboxed from your page's CSS, so var(--…) has nothing to resolve against. Theming via CSS variables requires the SVG markup to be inlined directly in the HTML.
Can the tool auto-detect the colours for me?
Yes — leave the mapping empty. At process time it collects every distinct #hex in the file (in first-appearance order) and assigns --svg-color-1, --svg-color-2, and so on. Note there is no pre-scan list of swatches to click in the UI; auto-assignment simply happens when you process with an empty map.
Can I choose my own variable names?
Yes. Add a mapping row, pick the colour, and type the name (e.g. --brand-primary). If you omit the leading --, the tool adds it. Named mappings produce far more maintainable output than the generic --svg-color-N auto names.
Why isn't my mapped colour being replaced?
Usually a literal-match miss: the picker emits a 6-digit hex but your SVG stores the colour as 3-digit shorthand, a named colour, or rgb(). The replace is a literal, case-insensitive string find of the hex, so the exact form must be present. Open the SVG in a text editor to confirm the colour is written as a 6-digit #hex.
Does it touch gradients and filters?
Yes, indirectly — because it replaces the literal hex everywhere it appears, stop-color on gradient stops and flood-color on filter primitives are rewritten too, as long as they use a hex value the tool is targeting.
Will it change my path geometry or strip metadata?
No. It's a colour-string transform only. Path d data, coordinates, viewBox, IDs, and any metadata are left untouched. If you also want to shrink the file, run svg-pro-minifier afterwards.
Should I add fallback values to the variables?
The tool emits bare var(--name). For robustness, hand-edit the output (or your inline copy) to var(--name, #fallback) so the icon still renders if the variable is undefined — useful when the SVG can appear before the theme stylesheet loads.
What file size can I process?
SVG tier limits apply: Free 5 MB, Pro 50 MB, Pro+Media 200 MB, Developer 2 GB. The tool itself requires Pro. Real icons and illustrations are kilobytes, so the practical limit is the tier gate, not the size.
How is this different from the hex swapper?
svg-hex-swapper replaces one hex with another fixed hex — useful for normalising or recolouring permanently. This injector replaces a hex with a var(--name) reference so the colour becomes themeable at runtime. Use the swapper to clean up colours first, then this tool to parameterise them.
Can I run this without the browser UI?
Yes. It's a server-safe engine tool. GET /api/v1/tools/svg-css-variable-injector returns the schema; pair the @jadapps/runner and POST { input, options: { variableMap: ["#003082:--brand-primary"] } } to http://127.0.0.1:9789/v1/tools/svg-css-variable-injector/run. See the automation guide for a full pipeline.
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.