很多个人站点的图标是「日积月累长出来」的:早年从某个免费素材站下载了三个 PNG 当菜单图标,后来改版时又从另一个站点抄来几个 SVG,同时呢,主题里还藏着一个 300 多 KB 的图标字体文件。这些东西单看都还能用,放在一起就会出三种问题:尺寸对不齐、颜色改不动、请求数比正文还多。这篇文章讲的是把散落的图标收回成一套系统,从选格式、做雪碧图、接进模板,到把 favicon 和各平台的图标一次配齐。
一、先盘点:你的图标现在有多少套体系
动手之前先花十分钟把仓库里所有跟图标有关的东西列出来。手工做法是在主题目录下按扩展名查找:
find usr/themes -type f \( -name "*.svg" -o -name "*.woff*" -o -name "*.ttf" \) | head -40
find usr/themes -type f -name "*.png" -size -20k | wc -l然后在浏览器里打开一篇详情页,按 F12 切到 Network 面板,用「Img」和「Font」两个筛选器看一遍实际加载了什么。重点记三个数字:图标类资源的总请求数、总体积、以及有几种不同的显示尺寸。个人站长常见的糟糕状态是:同一个搜索图标,在桌面导航里是 18px 的 PNG,在移动端抽屉里是 24px 的另一张 PNG,在页脚又是字体里的一个字形。
盘点完之后给每个图标标一个来源:素材站下载、从别的主题抄来、图标字体里的字形、还是 UI 框架自带的。来源决定了你要不要保留它。授权不明的一律替换掉——这是唯一一件不需要讨论优先级的事。
二、格式选择:能用 SVG 就别用图标字体
图标字体(iconfont)在 2015 年前后是主流方案,今天的个人站点没有理由再用它。原因有四个,都能实测出来:
第一是体积。一个装了 60 个图标的 woff2 大概 12 到 25 KB,而你实际可能只用到 8 个。SVG 内联之后每个图标不到 1 KB,只用 8 个就是 8 KB,而且不需要额外的字体文件请求。
第二是颜色和描边改不动。字体图标本质是一个字形,双色、渐变色、不同线宽的描边都要在制作字体时就定死,上线之后要靠 CSS 的伪元素叠两个字形去模拟。
第三是渲染模糊。字体图标在某些 Windows 的缩放比例下会出现半像素偏移,尤其是 125% 和 150% 缩放——这两种在国内的笔记本上非常常见。SVG 是矢量,任何缩放下都清晰。
第四是可访问性和兜底。字体没加载完的时候,页面上会闪出一个个方框,而 SVG 至少能保持空间、不跳动。
结论很直接:新做的图标一律 SVG,老字体文件里的图标用工具转出来,然后把字体文件从模板里删掉。转换可以用现成的在线工具,也可以直接找同一套开源的 SVG 图标包替换,比逆向提取字体可靠。
三、内联还是雪碧图:按「复用次数」分
SVG 放到页面上有两种方式,选错了会白花很多功夫。
方式一:内联 SVG。直接把 <svg viewBox="0 0 24 24">...</svg> 写进模板。优点是零请求、可以用 CSS 的 currentColor 继承文字颜色、可以用 CSS 变量控制粗细。缺点是每个图标都要占一段 HTML,同一个图标在页面上出现十次就重复十次。适合:页面里只出现一两次、需要跟文字颜色联动的图标,比如标题旁的小标记。
方式二:SVG 雪碧图。把所有图标塞进一个文件里的 <symbol>,页面上用 <use href="#icon-search"> 引用。整个站点的图标只有一个请求,浏览器还会缓存它。这是复用型图标(导航、按钮、列表里的通用符号)的标准做法。
雪碧图的正确构建方式是每个图标一个 <symbol>,带上自己的 viewBox,不要用 <g> 硬拼:
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="7"/><path d="M16 16l5 5"/>
</symbol>
<symbol id="icon-rss" viewBox="0 0 24 24" fill="currentColor">
<path d="M4 4a16 16 0 0 1 16 16h-4A12 12 0 0 0 4 8z"/>
</symbol>
</svg>页面里引用时要带上宽高和可访问性属性,否则在某些浏览器里 <use> 会渲染成零尺寸:
<svg class="icon" width="18" height="18" role="img" aria-label="搜索">
<use href="/assets/icons.svg#icon-search"></use>
</svg>一个常被忽略的细节是:如果雪碧图作为外部文件引用(icons.svg#id),跨域的时候浏览器会拒绝渲染,而且不会有任何报错,只是空白。所以要么把雪碧图放在同域名下,要么干脆内联进 HTML 顶部。同域名是最省事的选择。
四、CSS 统一收口:一个 class 管全部
图标系统能不能长期维护,取决于样式是否只有一处定义。建议只保留一个 .icon 类,尺寸用 CSS 变量控制:
.icon {
width: 1.15em;
height: 1.15em;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
vertical-align: -0.15em;
flex: none;
}
.icon-lg { --icon-size: 28px; width: var(--icon-size); height: var(--icon-size); }
.btn .icon { stroke-width: 1.8; }三条要点。第一,1.15em 让图标自动跟随周围文字的大小,移动端字体变小图标就跟着变小,不用写第二套尺寸。第二,vertical-align: -0.15em 是让图标在文字基线上视觉居中的经验值,纵深的偏移靠肉眼微调到合适为止,不要用 middle,那个值在不同字体下差异很大。第三,flex: none 防止图标在 flex 布局里被压扁——这是图标变形最常见的成因,尤其是窄屏下的按钮组。
颜色一律继承 currentColor,这样链接悬停、按钮激活、深色模式下图标颜色自动跟着变,不需要为每个状态写一条规则。如果某个图标必须保留自身的多色(比如品牌 logo),那就不要放进图标系统,单独当图片用,避免在 fill 上打补丁。
五、favicon 与各平台图标:一套源文件导全部
这是个人站点最容易漏的一块。浏览器标签页、桌面快捷方式、手机主屏、iOS 的分享面板、搜索结果里的小图标,用的其实是不同规格的文件,只做一个 favicon.ico 在今天已经不够看。
正确做法是先准备一张 512×512 的 SVG 源文件,再统一导出。建议的最小集:
favicon.ico:包含 16、32、48 三种尺寸,老浏览器和部分 RSS 阅读器还在用。favicon.svg:现代浏览器优先使用,支持深色模式下的双色变量,体积小。apple-touch-icon.png:180×180,iOS 加到主屏时用,务必不要透明背景,iOS 会把透明区域填成黑色。icon-192.png与icon-512.png:给 Android 和 PWA 用。site.webmanifest:声明上面这几个图标的用途和主题色。
HTML 里的写法顺序有讲究——多个图标声明同时存在时,浏览器按自己的偏好挑,但顺序会影响部分老版本的行为,把 .ico 放在最后常见于各家模板:
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="icon" href="/favicon.ico" sizes="any">验证方式非常朴素:手机浏览器打开站点,点「添加到主屏」,看图标是不是你的 logo 而不是网页截图;再用浏览器的开发者工具在 Application 面板的 Manifest 里看有没有报错。个人站点做这一套大概二十分钟,但它是访客把站点留在设备上的唯一视觉入口。
六、上线顺序与验收
一次性把所有图标全换掉风险不小,尤其是那个被导航、按钮、表单共用几十处的图标字体。稳妥的顺序是四步。
第一步,新建雪碧图并把常见图标先补进去,模板不动。第二步,挑一个改动面最小的位置(比如页脚的 RSS 图标)替换成 <use>,看渲染是否正常、颜色是否继承。第三步,按页面逐个替换:导航、文章头部操作区、列表页、表单按钮。每替换完一个页面,在手机尺寸下看一次,因为图标被压扁、错位的问题往往只在窄屏出现。第四步,确认全站没有引用之后再删掉字体文件,同时从 CSS 里删除字体声明。
验收可以抓三个数字。用开发者工具的 Network 面板对比改动前后的图标类请求数和总体积,正常情况下请求数应该减少一半以上。看一眼页面里还有没有 <img src="...png"> 形式的图标残留。最后在手机和桌面各截一张图,重点看导航和按钮里的图标是否与文字垂直居中——这一步人工检查,比任何自动化检查都可靠。
七、三个真实的坑
第一个坑是雪碧图里残留了设计工具的 <style> 和 class。从 Figma 或 Sketch 导出的 SVG 经常自带内联样式和帧名,塞进 <symbol> 之后,那些样式会作用到页面上的其他元素,导致某些按钮突然改变了描边。导出后一律用 svgo 清一遍,加 --multipass 参数,能去掉大半无用属性。
第二个坑是 viewBox 不一致。把 20×20 的图标和 24×24 的图标混在同一个雪碧图里,如果不给每个 <symbol> 单独写 viewBox,统一尺寸下会出现有的图标偏大有的偏小。这个问题在列表页的图标列里非常明显,一排看过去像没对齐。
第三个坑是颜色写死在 SVG 内部。素材站下载的 SVG 常常带着 fill="#333" 或 stroke="#000",一旦继承 currentColor 的规则被内部属性覆盖,深色模式下图标就变成一块看不清的黑。用 grep -o 'fill="#[0-9a-f]*"' assets/icons.svg 查一遍,把非 none 的固定色改成 currentColor,需要双色的那几个单独保留并明确标注。
八、小结
图标这件事的价值不在视觉,而在它是最典型的「散落资产」:不留心就会长出四套体系、三个字体文件、十几张不同尺寸的 PNG,然后每次改版都要重新摸一遍。收回成一套系统的成本大约是半天,收益是此后所有改版都只需要动一个雪碧图文件和一个 class。判断自己的工作是否算数,只看三个数字:图标请求数、图标资源总体积、以及页面里图标的尺寸种类。三个数字都降下来了,这套系统才算真的立住——下一步才有意义去谈颜色统一和暗色模式适配。