@genexus/svg-sass-generator
v1.2.0
Published
SVG Icon Generator: A Node.js tool for creating customizable SVG icons with color states like hover and active effects. It generates SVG icons and Sass placeholder selectors, simplifying integration into projects with generated CSS class names for scalabl
Keywords
Readme
svg-sass-generator
- What is this tool about?
- Key aspects this tools resolves
- Ice-cream icons transformation example
- Configuration file
- Common questions
- Final comments
What is this tool about?
Svg Sass Generator ('ssg' for short) is a tool that helps you manage multi-color or mono-chrome SVG icons across different UI states: enabled, hover, active, and disabled. It was created for managing icons on GeneXus Next, or for developers that collaborate with custom plugins.
It specially solves the common issue where masking + background color isn't enough when icons contain multiple figures with different colors. For any of both types (multi-color or mono-chrome) You define your colors palette for each state (enabled, hover, active, and disabled) and the tool generates new SVG's with the proper styling, that work also with dark or light themes.
Think of it like a modern SVG sprite system: one master file holds all stateful variations, and you simply use the version you need.

This shows how the icons work after being processed.
Key aspects this tools resolves:
1. Icons with one or multiple colors, in different states:
You give the tool your color choices and some SVG icons. It then creates SVG files ready to show these icons in different states like: enabled, hover, active, and disabled. It can also make versions for light and dark themes if you want.
2. CSS Custom Variables:
The tool creates special CSS variables for each icon’s state. For example, there’s a variable for the hover state of the system/user icon that you can use easily in your styles.
:root.light {
--icon__system_user--hover: url("#{$icons-path}system/light/user.svg#hover");
}
:root.dark {
--icon__system_user--hover: url("#{$icons-path}system/dark/user.svg#hover");
}3. An assets file for Chameleon
The Chameleon library includes a ch-image control that displays different icons based on state (e.g., focus, hover, active, disabled). This tool generates a TypeScript file with all icon definitions, allowing ch-image to automatically resolve the correct icon for each state.
Showing a multi-state icon with ch-image is really simple:
const USER_ICON = getIconPath({
category: "system",
name: "user",
}, "your-vendor-alias");
<ch-image src={USER_ICON}>🤔 What is Chameleon?
Chameleon is a collection of ready-made web components.
They are easy to customize, fast, lightweight, and work well for everyone. Chameleon is developed and maintained by GeneXus, and is the main components library used in GeneXus Next.
Ice Cream Icons Transformation Example
This example shows how to process both single-color and multi-color icons.
We picked ice cream icons because their scoops are a great way to show multi-color icons—or just one color if you want a one flavor ice cream!
📝
Configuration file parameters
Whether you’re processing single-color or multi-color icons, the tool needs a.jsconfig file with some settings and color palettes. In the examples below, we’ll focus on the color palettes. Other settings will be explained later.
Monochrome icons example
It’s best to keep icons organized in folders by category. We recommend creating one folder for each category of icons. Here, we have a folder called /ice-cream-cones with one single-color icon named regular-cone.svg:
/src
└── ice-cream-cones/
└── regular-cone.svgNext, in the configuration file, list the monochrome colors you want to use:
Monochrome palette:
export default {
config: {
// ... (details about config later)
},
icons: {
monochrome: {
colors: [
{
name: "blueberry",
states: {
enabled: { light: "#7a6ed3", dark: "#9d90ff" },
hover: { light: "#a59dec", dark: "#c1baff" },
active: { light: "#5a4fb8", dark: "#7a6ae6" },
disabled: { light: "#b1abc9", dark: "#48445c" },
},
},
{
name: "chocolate",
states: {
enabled: { light: "#7b4a1c", dark: "#b26b34" },
hover: { light: null, dark: null },
active: { light: null, dark: null },
disabled: { light: null, dark: null },
},
},
{
name: "chocolate-chip",
states: {
enabled: { light: "#9e7b6a", dark: "#bb9b89" },
hover: { light: null, dark: null },
active: { light: null, dark: null },
disabled: { light: null, dark: null },
},
},
{
name: "mango",
states: {
enabled: { light: "#ffb84c", dark: "#ffc266" },
hover: { light: "#ffd08b", dark: "#ffd999" },
active: { light: "#e6931a", dark: "#e6a23f" },
disabled: { light: "#d9c3a0", dark: "#5b4f38" },
},
},
{
name: "mint",
states: {
enabled: { light: "#5ec5a2", dark: "#8de4c2" },
hover: { light: "#a2e5d0", dark: "#c3f8e6" },
active: { light: "#3e9e7d", dark: "#64b89b" },
disabled: { light: "#aacac0", dark: "#45504e" },
},
},
{
name: "pineapple",
states: {
enabled: { light: "#fbe96a", dark: "#ffeb84" },
hover: { light: "#fef4a7", dark: "#fff3bb" },
active: { light: "#e5cb27", dark: "#ffd83d" },
disabled: { light: "#d1c58d", dark: "#5e583e" },
},
},
{
name: "strawberry",
states: {
enabled: { light: "#e85c85", dark: "#ff94b4" },
hover: { light: "#f5a0b7", dark: "#ffd0dd" },
active: { light: "#b03b64", dark: "#e2568e" },
disabled: { light: "#c9a2ab", dark: "#574f53" },
},
},
],
},
},
};🤔 Why are some values null for
chocolateandchocolate-chip?
A null means that state is not available. So,chocolateandchocolate-chiponly have theenabledstate.
Finally, tell the tool which icon folders are monochrome by listing them in the icons.categories array:
export default {
config: {
// ... (details about config later)
},
icons: {
monochrome: {
colors: [
// (Already provided above)
],
categories: [
{
name: "ice-cream-cones",
ignoredColors: ["mint", "pineapple"],
},
{
name: "other-direcoty-a",
ignoredColors: [],
},
{
name: "other-direcotry-b",
ignoredColors: [`mint`],
},
],
},
},
};🤔 What is
ignoredColorsfor?
It’s a list of colors the tool should skip for a specific folder.
In the example, the tool ignoresmintandpineapplecolors in the "ice-cream-cones" folder.
Theother-directory-afolder uses all colors, andother-directory-bonly ignoresmint. This helps you avoid extra XML or CSS you don’t need for certain categories.
svg source: src/ice-cream-cones/regular-cone.svg
This is the source icon, before being processed:
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<path fill="#9C9C9C" d="M43.57... continues"/>
</svg>svg output: generated/ice-cream-cones/light/regular-cone.svg
This is the output icon, after being processed:
| Icon | Enabled | Hover | Active | Disabled |
| ---------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| blueberry | |
|
|
|
|
chocolate | | ✖️ | ✖️ | ✖️ |
|
chocolate-chip | | ✖️ | ✖️ | ✖️ |
|
mango | |
|
|
|
|
strawberry | |
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<defs>
<g id="icon-def">
<path fill="var(--color)" d="M43.57... continues"></path>
</g>
</defs>
<view id="blueberry--enabled" viewBox="0 0 64 64" />
<use href="#icon-def" x="0" y="0" style="--color:#7a6ed3" />
<view id="blueberry--hover" viewBox="64 0 64 64" />
<use href="#icon-def" x="64" y="0" style="--color:#a59dec" />
<view id="blueberry--active" viewBox="128 0 64 64" />
<use href="#icon-def" x="128" y="0" style="--color:#5a4fb8" />
<view id="blueberry--disabled" viewBox="192 0 64 64" />
<use href="#icon-def" x="192" y="0" style="--color:#b1abc9" />
<view id="chocolate--enabled" viewBox="0 64 64 64" />
<use href="#icon-def" x="0" y="64" style="--color:#7b4a1c" />
<view id="chocolate-chip--enabled" viewBox="0 128 64 64" />
<use href="#icon-def" x="0" y="128" style="--color:#9e7b6a" />
<view id="mango--enabled" viewBox="0 192 64 64" />
<use href="#icon-def" x="0" y="192" style="--color:#ffb84c" />
<view id="mango--hover" viewBox="64 192 64 64" />
<use href="#icon-def" x="64" y="192" style="--color:#ffd08b" />
<view id="mango--active" viewBox="128 192 64 64" />
<use href="#icon-def" x="128" y="192" style="--color:#e6931a" />
<view id="mango--disabled" viewBox="192 192 64 64" />
<use href="#icon-def" x="192" y="192" style="--color:#d9c3a0" />
<view id="strawberry--enabled" viewBox="0 256 64 64" />
<use href="#icon-def" x="0" y="256" style="--color:#e85c85" />
<view id="strawberry--hover" viewBox="64 256 64 64" />
<use href="#icon-def" x="64" y="256" style="--color:#f5a0b7" />
<view id="strawberry--active" viewBox="128 256 64 64" />
<use href="#icon-def" x="128" y="256" style="--color:#b03b64" />
<view id="strawberry--disabled" viewBox="192 256 64 64" />
<use href="#icon-def" x="192" y="256" style="--color:#c9a2ab" />
</svg>Multicolor icons example
It’s best to make one folder for each icon category. We recommend creating one folder for each category of icons. Here, we have a folder /ice-cream-bowls with five different icons:
📝 These five icons look alike, but each has different colors, so they are treated as separate icons.
/src
└── ice-cream-bowls/
└── berry-patch.svg
└── choco-overload.svg
└── minty-breeze.svg
└── neapolitan-classic.svg
└── tropical-trio.svgNext, in the configuration file, list the multicolor colors you want to use:
Multicolor palette:
export default {
config: {
// ... (details about config later)
},
icons: {
multicolor: {
colors: [
{
name: "chocolate-chip",
states: {
enabled: { light: "#9e7b6a", dark: "#bb9b89" },
hover: { light: "#b89a8c", dark: "#d1b7a8" },
active: { light: "#7a5947", dark: "#9e7967" },
disabled: { light: "#c7bab2", dark: "#4f463f" },
},
},
{
name: "coconut",
states: {
enabled: { light: "#fefefe", dark: "#d9d9d9" },
hover: { light: "#f2f2f2", dark: "#bbbbbb" },
active: { light: "#e6e6e6", dark: "#ffffff" },
disabled: { light: "#cccccc", dark: "#666666" },
},
},
{
name: "cookies-cream",
states: {
enabled: { light: "#f3f1ef", dark: "#d8d5d2" },
hover: { light: "#e4e2e0", dark: "#c2bebc" },
active: { light: "#cfcac5", dark: "#a7a29e" },
disabled: { light: "#bcb8b3", dark: "#5b5752" },
},
},
// and so on...
],
},
},
};🍨 Berry Patch:
Before processing: This is the original "Berry Patch" ice cream icon:
src/ice-cream-bolws/berry-patch.svg
<svg class="berry-patch" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="cherry" fill="#9C9C9C" d="M33.2... continues"/>
<path class="blueberry" fill="#9C9C9C" d="M14.4... continues"/>
<path class="strawberry" fill="#9C9C9C" d="M40.8... continues"/>
<path class="bowl" fill="#9C9C9C" d="M42.6... continues"/>
</g>
</svg>After processing: This is the "Berry Patch" icon after the tool has prepared it for use:
generated/ice-cream-bolws/light/berry-patch.svg
| |
|
|
|
| :---------------------------------------------------------------: | :-----------------------------------------------------------: | :-------------------------------------------------------------: | :-----------------------------------------------------------------: |
| Enabled | Hover | Active | Disabled |
🍨 Choco Overload:
Before processing: This is the original "Choco Overload" ice cream icon:
src/ice-cream-bolws/choco-overload.svg
<svg class="choco-overload" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="cookies-cream" fill="#9C9C9C" d="M33.2... continues"/>
<path class="milk-chocolate" fill="#9C9C9C" d="M14.4... continues"/>
<path class="dark-chocolate" fill="#9C9C9C" d="M40.8... continues"/>
<path class="bowl" fill="#9C9C9C" d="M42.6... continues"/>
</g>
</svg>After processing: This is the "Choco Overload" icon after the tool has prepared it for use:
generated/ice-cream-bolws/light/choco-overload.svg
| |
|
|
|
| :------------------------------------------------------------------: | :--------------------------------------------------------------: | :----------------------------------------------------------------: | :--------------------------------------------------------------------: |
| Enabled | Hover | Active | Disabled |
🍨 Minty Breeze:
Before processing: This is the original "Minty Breeze" ice cream icon:
src/ice-cream-bolws/minty-breeze.svg
<svg class="minty-breeze" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="blueberry" fill="#9C9C9C" d="M33.28..."/>
<path class="milk-chocolate" fill="#9C9C9C" d="M14.44..."/>
<path class="mint" fill="#9C9C9C" d="M40.86..."/>
<path class="bowl" fill="#9C9C9C" d="M42.68..."/>
</g>
</svg>After processing: This is the "Minty Breeze" icon after the tool has prepared it for use:
generated/ice-cream-bolws/light/minty-breeze.svg
| |
|
|
|
| :----------------------------------------------------------------: | :------------------------------------------------------------: | :--------------------------------------------------------------: | :------------------------------------------------------------------: |
| Enabled | Hover | Active | Disabled |
🍨 Neapolitan Classic:
Before processing: This is the original "Neapolitan Classic" ice cream icon:
src/ice-cream-bolws/neapolitan-classic.svg
<svg class="neapolitan-classic" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="vanilla" fill="#9C9C9C" d="M33.28..."/>
<path class="chocolate" fill="#9C9C9C" d="M14.44..."/>
<path class="strawberry" fill="#9C9C9C" d="M40.86..."/>
<path class="bowl" fill="#9C9C9C" d="M42.68..."/>
</g>
</svg>After processing: This is the "Neapolitan Classic" icon after the tool has prepared it for use:
generated/ice-cream-bolws/light/neapolitan-classic.svg
| |
|
|
|
| :----------------------------------------------------------------------: | :------------------------------------------------------------------: | :--------------------------------------------------------------------: | :------------------------------------------------------------------------: |
| Enabled | Hover | Active | Disabled |
🍨 Tropical Trio:
Before processing: This is the original "Tropical Trio" ice cream icon:
src/ice-cream-bolws/tropical-trio.svg
<svg class="tropical-trio" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="pineapple" fill="#9C9C9C" d="M33.28..."/>
<path class="coconut" fill="#9C9C9C" d="M14.44..."/>
<path class="mango" fill="#9C9C9C" d="M40.86..."/>
<path class="bowl" fill="#9C9C9C" d="M42.68..."/>
</g>
</svg>After processing: This is the "Tropical Trio" icon after the tool has prepared it for use:
generated/ice-cream-bolws/light/tropical-trio.svg
| |
|
|
|
| :-----------------------------------------------------------------: | :-------------------------------------------------------------: | :---------------------------------------------------------------: | :-------------------------------------------------------------------: |
| Enabled | Hover | Active | Disabled |
Configuration file
The tool requires a .js configuration file, that defines a set of parameters, and the colors values for the icons. You can name the file as you like, since you will have to run a script referncing this file. In the following example, we are calling this file config.js.
📂 Note about paths: All paths in
config.jsshould be relative to the current working directory—that is, the directory from which you run the SVG Sass generator script. This is usually the root of your project, typically wherepackage.jsonis located.
config.js:
export default {
config: {
enableLight: true, // If true, it will process light icons
enableDark: true, // If true, it will process dark icons
svgSrcDir: "./test/icons/src/", // The directory where your source icons/folders reside
svgOutDir: "./docs/readme/", // The output directory where your icons will be generated
sassOutDir: "./test/sass/", // The output directory where your sass files will be generated
showcaseFilePath: "./test/showcase.html", // The .html filepath for output icons showcase
objectFilePath: "./test/ICON_ASSETS.ts", // The .ts filepath for the icons assets file
logFilePath: "./test/output.log", // The .log filepath for the log file. It keeps track of what happened during the process.
defaultMonochromeColor: "chocolate", // The default color used for monochrome icons.
// This is especially useful when using the Chameleon `ch-image` component,
// as omitting the `colorType` will apply the `defaultMonochromeColor` to the icon.
vendorAlias: null, // An optional vendor alias prefixed to the icon’s CSS variables.
// Useful for preventing naming collisions between icons from different libraries.
},
icons: {
monochrome: {
colors: [
{
name: "sky-blue",
states: {
enabled: { light: "#4a6fa5", dark: "#3c5b84" },
hover: { light: "#6c8fc0", dark: "#5c79a1" },
active: { light: "#3a5c8a", dark: "#2f4a6d" },
disabled: { light: "#a3b7cd", dark: "#404e62" },
},
},
{
name: "fire-red",
states: {
enabled: { light: "#ff6b3d", dark: "#cc5430" },
hover: { light: "#ff8a61", dark: "#e26547" },
active: { light: "#e6532d", dark: "#b24528" },
disabled: { light: "#f3b09a", dark: "#4a342f" },
},
},
{
name: "candy-pink",
states: {
enabled: { light: "#da5ca2", dark: "#b04985" },
hover: { light: "#e57db5", dark: "#c6629a" },
active: { light: "#b64c85", dark: "#903c6c" },
disabled: { light: "#e3a7c8", dark: "#51384f" },
},
},
// add more as needed...
],
categories: [
// List each monochrome icon directory here along with the colors it should ignore.
{
name: "system",
ignoredColors: ["fire-red", "candy-pink"],
},
{
name: "backend",
ignoredColors: [],
},
{
name: "navigation",
ignoredColors: ["sky-blue"],
},
],
},
multicolor: {
colors: [
{
name: "leaf-green",
states: {
enabled: { light: "#5caa69", dark: "#3e814b" },
hover: { light: "#78c182", dark: "#509c60" },
active: { light: "#459357", dark: "#2e6a3d" },
disabled: { light: "#a6d1aa", dark: "#3a4e3c" },
},
},
{
name: "leaf-green",
states: {
enabled: { light: "#5caa69", dark: "#3e814b" },
hover: { light: "#78c182", dark: "#509c60" },
active: { light: "#459357", dark: "#2e6a3d" },
disabled: { light: "#a6d1aa", dark: "#3a4e3c" },
},
},
{
name: "honey-yellow",
states: {
enabled: { light: "#f6c744", dark: "#c6a836" },
hover: { light: "#f8d76d", dark: "#d7ba55" },
active: { light: "#e3b832", dark: "#a88e25" },
disabled: { light: "#f3dea5", dark: "#554b2e" },
},
},
// add more as needed...
],
},
},
};📝 Tip: If you’re using the same colors for both monochrome and multicolor, consider defining a shared color array and referencing it in both places to avoid duplication:
const mySharedColors = [
{
name: "sky-blue",
states: {
enabled: { light: "#4a6fa5", dark: "#3c5b84" },
hover: { light: "#6c8fc0", dark: "#5c79a1" },
active: { light: "#3a5c8a", dark: "#2f4a6d" },
disabled: { light: "#a3b7cd", dark: "#404e62" },
},
},
{
name: "fire-red",
states: {
enabled: { light: "#ff6b3d", dark: "#cc5430" },
hover: { light: "#ff8a61", dark: "#e26547" },
active: { light: "#e6532d", dark: "#b24528" },
disabled: { light: "#f3b09a", dark: "#4a342f" },
},
},
{
name: "candy-pink",
states: {
enabled: { light: "#da5ca2", dark: "#b04985" },
hover: { light: "#e57db5", dark: "#c6629a" },
active: { light: "#b64c85", dark: "#903c6c" },
disabled: { light: "#e3a7c8", dark: "#51384f" },
},
},
];
export default {
config: {
// config properties here
},
icons: {
monochrome: {
colors: myColors,
categories: [
{
name: "system",
ignoredColors: ["fire-red", "candy-pink"],
},
{
name: "backend",
ignoredColors: [],
},
{
name: "navigation",
ignoredColors: ["sky-blue"],
},
],
},
multicolor: {
colors: myColors,
},
},
};Overriding config. parameters
Sometimes you may need to set a dynamic value for a configuration property. In these cases, it's useful to override one or more parameters by passing them as arguments to the script that runs the tool.
For example, to override the source directory defined in the config file, you can do the following:
package.json
"scripts": {
"process-icons": "ssg --configPath=./src/config.js --svgSrcDir=other-directory/my-icons/src"
},config.js
config: {
svgSrcDir: "./src/icons/src/", // This path will have no effect. It is overrided by "other-directory/my-icons/src"
// other parameters...
},or you can override all of them:
package.json
"scripts": {
"process-icons": "ssg --configPath=./src/config.js--enableLight --enableDark --svgSrcDir=other-directory/icons/src/ --svgOutDir=other-directory/icons/_generated/ --sassOutDir=other-directory/sass/ --showcaseFilePath=other-directory/showcase.html --objectFilePath=other-directory/ICON_ASSETS.ts --logFilePath=other-directory/output.log --defaultMonochromeColor=chocolate --vendorAlias=mac"
},📝 Memo: Defining configuration parameters in the config.js file is cleaner and preferred. However, if you need to override a value, you can pass it as an argument through the script.
Common questions:
Q: Does the tool generate one SVG file per state?
A: No. Each icon is one SVG file that includes all states: enabled, hover, active, and disabled.
Q: Does each SVG file include both light and dark themes?
A: No. You get one SVG for the light theme and another separate SVG for the dark theme. This helps load only what the user needs.
Q: Do I have to create both light and dark themes?
A: No. You can choose to generate one or both themes by switching a setting.
Q: Can I create more themes besides light and dark?
A: Not yet.
Q: Does the tool support fill and stroke colors?
A: Yes, it handles shapes with fill, stroke, or both.
Q: My output icons appear will a full background color, something seems not working. What is happening?
A: Probably, you have a container element (a box) with a fill on your source icon, and because of this the tool is also filling this element with a color. Try setting setting opacity 0% on this container element.
Q: What settings do I need to configure to use this tool? A: This tool is designed for GeneXus Next, so no setup is needed for general use. However, if you're developing plugins for GeneXus Next, you should define your own vendor alias in this tool’s configuration file and use that alias when referencing your icons.
ie.:
config.js
export default {
config: {
// ... other settings
vendorAlias: "your-vendor-alias",
},your component
const USER_ICON = getIconPath({
category: "system",
name: "user",
}, "your-vendor-alias");
<ch-image src={USER_ICON}>Q: I want to report a bug or suggest an improvement. How can I get in touch?
A: For now, you can email [email protected]
Final comments
SVG Export Plugin
Since multicolor icons require each element to have a class name that matches a color, we recommend designing your icons in Figma and exporting them using the SVG Export Plugin. This plugin automatically applies layer names as class names in the exported SVG, saving you time and ensuring consistency. For example:
![]()
choco-overload.svg (exported with SVG Export)
<svg class="choco-overload" xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64">
<g class="group">
<path class="dark-chocolate" fill="#9C9C9C" d="M40.8... continues"/>
<path class="milk-chocolate" fill="#9C9C9C" d="M14.4... continues"/>
<path class="cookies-cream" fill="#9C9C9C" d="M33.2... continues"/>
<path class="bowl" fill="#9C9C9C" d="M42.6... continues"/>
</g>
</svg>As you can see, exporting your icons with SVG Export allows you to automatically use your vector names as CSS class names in the SVGs. This streamlines the workflow and eliminates the need to rename classes manually. Additionally, using this plugin significantly reduces the SVG file size.
This readme showcase
Checkout this readme icons example showcase on this link.
