惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
G
Google Developers Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
Cloudbric
Cloudbric
L
LINUX DO - 最新话题
MyScale Blog
MyScale Blog
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
S
Security @ Cisco Blogs
Latest news
Latest news
I
Intezer
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Threatpost
博客园 - 【当耐特】
S
Schneier on Security
P
Privacy International News Feed
G
GRAHAM CLULEY
T
Tenable Blog
AWS News Blog
AWS News Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
博客园 - Franky
Engineering at Meta
Engineering at Meta
美团技术团队
S
Secure Thoughts
T
Troy Hunt's Blog
Microsoft Security Blog
Microsoft Security Blog
SecWiki News
SecWiki News
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Anthony Fu

Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony's Roads to Open Source - The Set Theory (React ver.) Mental Health in Open Source The Evolution of Shiki v1.0 The Magic in Shiki Magic Move Anthony's Roads to Open Source - The Progressive Path Anthony Fu Anthony Fu Anthony Fu Anthony's Roads to Open Source - The Set Theory Now, and the Future of Nuxt Devtools Anthony's Roads to Open Source - The Set Theory Anthony Fu Anthony Fu Stable Diffusion QR Code 101 Refining AI Generated QR Code Stylistic QR Code with Stable Diffusion Anthony Fu How I Manage GitHub Notifications Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Dev SSR on Nuxt with Vite Why I don't use Prettier Anthony Fu Anthony Fu Ship ESM & CJS in one Package Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Reflection of Speaking in Public Anthony Fu Windi CSS and Tailwind JIT Typed Provide and Inject in Vue Color Scheme for VS Code Anthony Fu Anthony Fu Anthony Fu Anthony Fu Anthony Fu Destructuring... with object or array? Anthony Fu Anthony Fu Make Libraries Working with Vue 2 and 3 Anthony Fu Anthony Fu Anthony Fu Anthony Fu
Anthony Fu
Anthony Fu · 2021-10-31 · via Anthony Fu
  • My Icon Explorations
  • Prior Arts
  • The Idea
  • Make it Work
    • DataURI
    • Scalable
    • Colorable
    • Icons with Colors
  • Use It

中文 Chinese Version

In my previous post about Reimagine Atomic CSS, I introduced a preset of UnoCSS that provides the ability to use any icons on-demand in purely CSS. Today in this post, I’d like to share with you how we made it possible.

My Icon Explorations

If you are interested in how I get here, there is an index of my previous post about the stories of my icon explorations and experiments.

Prior Arts

I know there is a Pure CSS icon solution called css.gg, which is a great idea to use pseudo-elements (::before, ::after) to construct the icons. However, that could require some expert knowledge of how CSS works, but I imagine that approach could be hard to create more complex icons. Instead of the limited choices in a specific set, I am seeking a more general solution that could apply to any icons.

The Idea

The idea come from this feature request created by @husayt to unplugin-icons and the initial implementation in this pull request by @userquin. The idea here is quite straightforward - to generate CSS with the icons in DataURI as the background image.

.my-icon {
  background: url(data:...) no-repeat center;
  background-color: transparent;
  background-size: 16px 16px;
  height: 16px;
  width: 16px;
  display: inline-block;
}

With that, we could use any images inlined in CSS with a single class.

<div class="my-icon"></div>

It’s indeed an interesting idea. However, this is more like an image instead of an icon. To me, an icon has to be scalable and colorable (if it’s monochrome).

Make it Work

DataURI

Thanks again to Iconify, which unified 100+ icon sets with 10,000+ icons into the consistent JSON format. It allows us to get the SVG of any icon set by simply providing the collection and icon ids. The usage is like this:

import { getIconData, iconToSVG } from '@iconify/utils'

const svg = iconToSVG(getIconData('mdi', 'alarm'))
// (this is not the exact API, simplified here for demo)

Once we got the SVG string, we could convert the it to DataURI:

const dataUri = `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`

Talking about DataURI, it’s almost the default choice to use Base64 until I read Probably Don’t Base64 SVG by Chris Coyier. Base64 is needed to encode binary data like images to be used in plain text files like CSS, while for SVG, since it’s already in text format, the extra encoding to Base64 actually makes the file size larger.

Combine the technique mentioned in Optimizing SVGs in data URIs by Taylor Hunt to improve the output size, further, here is the solution we end up with.

// https://bl.ocks.org/jennyknuth/222825e315d45a738ed9d6e04c7a88d0
function encodeSvg(svg: string) {
  return svg.replace('<svg', (svg.includes('xmlns') ? '<svg' : '<svg xmlns="http://www.w3.org/2000/svg"'))
    .replace(/"/g, '\'')
    .replace(/%/g, '%25')
    .replace(/#/g, '%23')
    .replace(/\{/g, '%7B')
    .replace(/\}/g, '%7D')
    .replace(/</g, '%3C')
    .replace(/>/g, '%3E')
}

const dataUri = `data:image/svg+xml;utf8,${encodeSvg(svg)}`

Scalable

The first step of making the "image" more like an icon, we need to make it scalable to the context.

Luckily we have the first-class support scaling support - the em unit.

.my-icon {
  background: url(data:...) no-repeat center;
  background-color: transparent;
  background-size: 100% 100%;
  height: 1em;
  width: 1em;
}

By changing the height and width to 1em, and the background-size to 100%, we made the image scales based on the parent’s font size.

  • Small
  • Normal
  • Large

Colorable

In inlined SVG, we could use fill="currentColor" to make the color of the SVG matches with the current text color. However, when we use it as a background image, it becomes a flat image. The dynamic parts of the SVG are lost, so is the currentColor magic (it’s just like you can’t override the color of a PNG).

If you do a quick search, you will find that most people are telling you that you can’t. Some might offer you the option to assign the colors in the SVG before converting to DataURI, which could solve the specific problem that you want the icon to have color, but not the root cause that the color is not reactive to the context.

Then you might come up with the idea of using CSS filters, like Una Kravets mentioned in Solved with CSS! Colorizing SVG Backgrounds. That sounds valid, but only that you need to calculate the matrix of how to transform the color to the desired ones. Probably feasible by introducing some runtime JavaScript for that? Maybe, if so, we lost the whole point of trying icons in pure CSS.

This sounds like a dead-end to me. Until I accidentally found the article Coloring SVGs in CSS Background Images by Noah Blon. In the article, Noah mentioned a brilliant idea of using CSS masks - a property that I have never heard of before.

.my-icon {
  background-color: red;
  mask-image: url(icon.svg);
}

Instead of using the icon as a background image and figuring out a way to color it, we could actually use the icon as a mask to clip the filled background color. Furthermore, we could now use the currentColor magic to have the icon matching with the parent text color!

.my-icon {
  background-color: currentColor;
  mask-image: url(icon.svg);
}

This is a blue text, with the blue icon

Icons with Colors

We made the monochrome icons colorable but now it problem comes to the icons with colors. With the mask approach, the colors and content of the icons got lost, for example:

Yes, I might say it’s hard for one approach to cover all the cases.

Unless - you could blend two approaches into one! Remember we just talked about the background image approach serving the icons as images? Isn’t that just what we want for colored icons? - We don’t need to change the colors after all!

So the solution is actually pretty simple, we just need to find a way to distinguish the monochrome and colored icons smartly. Luckily, since we had access the the SVG content, we could have:

// if an SVG icon have the `currentColor` value,
// it's very likely to be a monochrome icon
const mode = svg.includes('currentColor')
  ? 'mask'
  : 'background-img'

const uri = `url("data:image/svg+xml;utf8,${encodeSvg(svg)}")`

// monochrome
if (mode === 'mask') {
  return {
    'mask': `${uri} no-repeat`,
    'mask-size': '100% 100%',
    'background-color': 'currentColor',
    'height': '1em',
    'width': '1em',
  }
}
// colored
else {
  return {
    'background': `${uri} no-repeat`,
    'background-size': '100% 100%',
    'background-color': 'transparent',
    'height': '1em',
    'width': '1em',
  }
}

And it works surprisingly well! You know, it’s now behavior similar to the thing we are using daily - system’s native emojis. The color of texts changes based on the context, while emojis stay the colors of their own.

Here are some showcases of what we end up with:

Material Design

Carbon

Tabler

Twemoji

Logos

To see and find all the icons available, you can check out my other project Icônes.

Use It

If you want to try this icons solution in your project, you can install UnoCSS and the icons preset:

npm i -D unocss @unocss/preset-icons @iconify/json

@iconify/json is the package that stores the icon data from Iconify. Alternatively, you could install per icon set, for example, @iconify-json/mdi for Material Design Icons or @iconify-json/carbon for Carbon Icons and so on.

Then in your vite.config.js

import UnocssIcons from '@unocss/preset-icons'
import UnoCSS from 'unocss'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    UnoCSS({
      // when `presets` is specified, the default preset will be disabled
      // so you could only use the pure CSS icons in addition to your
      // existing app without polluting other CSS
      presets: [
        UnocssIcons({
          // options
          prefix: 'i-',
          extraProperties: {
            display: 'inline-block'
          }
        }),
        // presetUno() - if you want to use other atomic CSS as well
      ],
    }),
  ],
})

And that’s it for today. Hope you enjoy this icons solution from UnoCSS, or get some inspiration from it for your own projects.

Thanks for reading, and see you :)