Frontend Development 11 min read

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.

ByteDance Web Infra
ByteDance Web Infra
ByteDance Web Infra
Pure CSS Icons with UnoCSS: DataURI, Scaling, Coloring, and Masking Techniques

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/json

You 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!

frontendWeb DevelopmentCSSDataURIIconsUnoCSS
ByteDance Web Infra
Written by

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

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.