SVG tips and tricks #1413
Replies: 2 comments 2 replies
-
You pretty much can do whatever you like with SVGs now already. Maybe you can provide a few test cases and I will show how to implement it — this could turn into a helpful knowledge base. Problem with inlining SVGs is performance (DOM manipulation) and security (sanitizing). That's why I see current named icon with USE tag approach for built-in Taiga UI icons as preferable, but you are not limited to it. It also has a benefit of easily configurable native cache. I wonder if we can/need to improve caching of inlining SVGs mechanics we have in place now 🤔 |
Beta Was this translation helpful? Give feedback.
-
Hi! I can confirm, it took some while to find a solution for us. So, here an example of using Material Icons: // Installed from: https://marella.github.io/material-design-icons/demo/svg/
npm i @material-design-icons/svg We are not using the original Github repo for getting the icons, but a tiny lib which preprocess the icons and makes them easier to consume. Added a script to make those icons "responsive": import fs from "fs";
import glob from "glob";
import path from "path";
const PATH_TO_MATERIAL_ICONS = "./node_modules/@material-design-icons/svg/outlined/";
const OUTPUT_PATH = "./src/assets/icons/material/";
processIcons();
function processIcons() {
console.info("\x1b[34m%s\x1b[0m", "Start transforming Material Icons: \n");
glob(PATH_TO_MATERIAL_ICONS + "*.svg", {}, (err, files) => {
files.forEach(file => {
const fileName = path.posix.basename(file);
const src = String(fs.readFileSync(file));
const name = fileName.replace(".svg", "");
const wrapped = wrapIcon(src, name);
const final = `<svg xmlns="http://www.w3.org/2000/svg" width="${wrapped.width}" height="${wrapped.height}">${wrapped.src}</svg>`;
fs.writeFileSync(OUTPUT_PATH + fileName, final);
console.info("\x1b[32m%s\x1b[0m", `[preprocessed]:`, fileName);
});
});
}
function wrapIcon(source, name) {
const svgRegex = /^\s*<svg.*?>/;
// all Material icons have size 24x24px, so we can replace with a responsive version
const replacer = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">';
const responsiveSource = source.replace(svgRegex, replacer);
return {
// we put a font based size also to the outer SVG, so that we can use it without Taiga UI SVG component
width: "1em",
height: "1em",
src: `<g id="${name}" xmlns="http://www.w3.org/2000/svg">${responsiveSource}</g>`,
};
} Put it into a Configure TUI_ICONS_PATH export const ICON_MAPPING: Record<string, string> = {
// Taiga UI - Overridden by Material Icons
// ***********
tuiIconAlertCircleLarge: 'error_outline',
tuiIconArrowDown: 'arrow_downward',
tuiIconArrowLeft: 'arrow_back',
tuiIconArrowRight: 'arrow_forward',
tuiIconArrowUp: 'arrow_upward',
//...
// Custom
// ***********
fooAcme: 'acme_icon_whatever',
};
/**
* Maps icon name to path to icon. Is attached to `TUI_ICONS_PATH`.
* It requires that icons were properly processed and are available under the specified path.
* By default, we map material icon names directly to file name because they are the same.
* But for the internal Taiga icons and some specific ones of us, we can use a mapping.
*
* @param name - Name of icon (for usage in Taiga UI)
* @return - Path to icon
*/
export function iconsPath(name: string): string {
// first, we look up a mapping
const mapped = ICON_MAPPING[name];
if (mapped) {
// we found a mapping of Taiga UI -> so it is a Material icon
if (name.startsWith('tui')) {
return `assets/icons/material/${mapped}.svg#${mapped}`;
}
// we found a mapping of our icons
if (name.startsWith('foo')) {
return `assets/icons/foo/${mapped}.svg#${mapped}`;
}
}
// we map the remaining icons directly to Material icons
name = mapped ?? name;
return `assets/icons/material/${name}.svg#${name}`;
} Add some CSS which we need to tweak Taiga UI CSS: // Overwrite Taiga UI settings to have responsive icons
tui-svg {
height: 1em !important;
width: 1em !important;
font-size: 1.5rem !important; // 24px by default
}
// Apply specific icon sizes to button sizes
[tuiButton][data-tui-host-size="xl"] tui-svg {
font-size: 2rem !important; // 32px
}
[tuiButton][data-tui-host-size="l"],
[tuiButton][data-tui-host-size="m"] {
tui-svg {
font-size: 1.5rem !important; // 24px
}
}
[tuiButton][data-tui-host-size="s"] tui-svg {
font-size: 1.25rem !important; // 20px
}
[tuiButton][data-tui-host-size="xs"] {
tui-svg {
font-size: 1rem !important; // 16px
}
// adept gaps of icon / label
.left {
margin: 0 0.25rem 0 -0.1rem !important;
}
.right {
margin: 0 -0.1rem 0 0.25rem !important;
}
}
[tuiIconButton][data-tui-host-size="s"],
[tuiIconButton][data-tui-host-size="xs"] {
.left, .right {
margin: 0 !important;
}
} Now, we don't need two different icons and we are able to scale them via But all the Best regards, |
Beta Was this translation helpful? Give feedback.
-
I learned something cool from the FontAwesome source code. Together with their angular package.
I always liked the idea of styling
icons
usingcolor
andfont-size
.To make this possible with and SVG. We need to set
width: 1em
height: 1em
fill: currentColor
.I like the way they use the embedded svg's (no network calls) in a tree shakeable manner.
If Taiga could follow a similar approach and cater for custom icons definitions. this would be a game changer. Would be quite cool to reference any SVG whether it comes from
FontAwesome
. Taiga, or even a custom svgBeta Was this translation helpful? Give feedback.
All reactions