favicon 小冒险

3 分钟
favicon 小冒险

配图:self-drawn illustration

博客上线后,我把整站的视觉细节捋了一遍,最后停在那个最小也最容易被忽视的元素上:浏览器标签页里的 favicon。

第一个目标:明暗自适应

Chrome、Firefox 的标签栏有明暗两种主题,一个黑色图标在暗色标签栏里会直接隐身。SVG favicon 支持内嵌媒体查询,可以做 到「一份文件、两副面孔」:

<style>
  path { fill: #0c0c0c; }                     /* 浅色标签栏: 深色图标 */
  @media (prefers-color-scheme: dark) {
    path { fill: #fafafa; }                    /* 深色标签栏: 米白图标 */
  }
</style>

现代浏览器都认这套,Safari 除外——它只认 ICO。所以还需要一套静态兜底:ICO 和苹果触屏图标统一用品牌深底配色,反正 Safari 用户看到的是固定的,那就让它固定得体面。

PIL 的 ICO 之坑

静态图标我用 Pillow 生成。想当然的写法是拿最大那张图分别缩放存进去:

img.save("favicon.ico", sizes=[(16,16),(32,32),(48,48),(64,64)])

结果文件只有 186 字节——PIL 悄悄只存了一帧,而且是最小的那种。它不会报错,不会警告,只会让你在线上打开放大后发现糊成一团。

正确姿势是以目标尺寸为基准逐张生成,最大的那张作为基底:

base = draw_monogram(64)          # 以最大尺寸作画
base.save("favicon.ico",
          sizes=[(16,16),(32,32),(48,48),(64,64)])

一晚上的调试换来一条朴素的经验:多帧 ICO 的大小应该和帧数成正比。186 字节的多尺寸 ICO,一定是哪里不对。

自查清单

图标这种小东西,改完不等于做对。我的自查流程:

  1. 多底色预览:把 96px 图标分别贴在浅色、深色的模拟标签栏上,缺一个都不算检查过
  2. 小尺寸放大:16px 的图 nearest 放大 4 倍看锯齿和可辨识度
  3. 线上复核:部署后 curl -I 三个格式(svg/ico/png)都 200,再开无痕窗口强刷

值得吗

一个 32×32 的图标,前后折腾了一晚。理性说,几乎没有访客会注意它;但打开自己站点时标签栏里那个清晰的小方块,是整个网站给我的第一眼反馈。

自托管的乐趣,一半来自访客看到的东西,另一半来自只有自己知道的地方。

Suggest an edit

最后更新:30 Aug 2026