Pure CSS Icons with UnoCSS: DataURI, Scaling, Coloring, and Masking Techniques
This article explains how to embed any SVG icon in pure CSS using UnoCSS presets, DataURI encoding, scalable em‑based sizing, context‑aware coloring via CSS masks, and provides practical code examples for both monochrome and multicolor icons.
Thanks to QC‑L for translating the original article and to the English version author.
My Icon Exploration Journey
I have written several posts documenting my experiments with icon solutions, including the 2020 "Icon Exploration Journey" and follow‑up posts in 2021.
Existing Solutions
A pure‑CSS icon library called css.gg builds icons using only ::before and ::after pseudo‑elements. While it demonstrates deep CSS knowledge, it is limited to three elements per icon and cannot easily create complex icons.
My Solution
The idea originates from community member @husayt who raised a request in unplugin-icons . @userquin provided an initial implementation in a pull request. The solution uses icons from Iconify as DataURIs and generates the following CSS:
.my-icon {
background: url(data:...) no-repeat center;
background-color: transparent;
background-size: 16px 16px;
height: 16px;
width: 16px;
display: inline-block;
}This allows any image to be embedded as a background class.
Implementation
DataURI
Iconify provides over 100 icon collections unified in a JSON format. You can retrieve an SVG with iconToSVG(getIconData('mdi', 'alarm')) and then convert it to a DataURI:
const dataUri = `data:image/svg+xml;base64:${Buffer.from(svg).toString('base64')}`Base64 encoding is often unnecessary for SVG because it is already text; a UTF‑8 encoded DataURI is smaller.
Using Taylor Hunt's optimisations, the final encoding function looks like:
function encodeSvg(svg: string) {
return svg.replace('
/g, '%3E')
}
const dataUri = `data:image/svg+xml;utf8,${encodeSvg(svg)}`Scalable
To make the image behave like an icon, set its size using em units and background-size: 100% 100% :
.my-icon {
background: url(data:...) no-repeat center;
background-color: transparent;
background-size: 100% 100%;
height: 1em;
width: 1em;
}This makes the icon scale with the surrounding font size.
Colorable
Embedding the SVG directly allows the use of fill="currentColor" for context‑aware coloring, but when the SVG is used as a background image the currentColor effect is lost.
One workaround is to apply CSS masks. By using the SVG as a mask and setting background-color: currentColor , the icon inherits the text color:
.my-icon {
background-color: currentColor;
mask-image: url(icon.svg);
}Colored Icons
For multicolor icons, keep the SVG as a normal background image. The implementation decides the mode based on whether the SVG contains currentColor :
// Determine mode
const mode = svg.includes('currentColor') ? 'mask' : 'background-img';
if (mode === 'mask') {
return {
mask: `url("data:image/svg+xml;utf8,${encodeSvg(svg)}") no-repeat`,
'mask-size': '100% 100%',
'background-color': 'currentColor',
height: '1em',
width: '1em',
};
} else {
return {
background: `url("data:image/svg+xml;utf8,${encodeSvg(svg)}") no-repeat`,
'background-size': '100% 100%',
'background-color': 'transparent',
height: '1em',
width: '1em',
};
}This approach yields results similar to native Emoji: monochrome icons follow the text color, while colored icons retain their original colors.
Usage
To try this solution, install UnoCSS and the icon preset:
npm i -D unocss @unocss/preset-icons @iconify/jsonYou can also install individual icon collections, e.g., @iconify-json/mdi for Material Design icons.
Configure vite.config.js as follows:
import { defineConfig } from 'vite'
import Unocss from 'unocss'
import UnocssIcons from '@unocss/preset-icons'
export default defineConfig({
plugins: [
Unocss({
presets: [
UnocssIcons({
prefix: 'i-',
extraProperties: { display: 'inline-block' }
})
// presetUno() – uncomment to enable default preset
],
}),
],
})That’s the complete guide. I hope this UnoCSS icon solution inspires you for your own projects.
Thank you for reading, see you next time!
ByteDance Web Infra
ByteDance Web Infra team, focused on delivering excellent technical solutions, building an open tech ecosystem, and advancing front-end technology within the company and the industry | The best way to predict the future is to create it
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.