你以为 SVG 只能靠复制文件才能换颜色?今天一个 CSS 规范把它彻底原生化了

配了三年设计系统,今天才发现同一个 SVG 文件要在三个地方显示三种颜色这件事,从来没有标准解——inline 样式最多,外链文件永远是黑的。

以前你只有三条路:把 SVG 直接写在 HTML 里(每次换主题要改源码)、维护三份颜色不同的 SVG 文件(一个图标三份副本)、或者用 JavaScript 动态注入样式(性能开销另算)。无论哪条,SVG 的可缓存性和样式隔离都打了个对折。

W3C 在今年 7 月 14 日发布了 CSS Linked Parameters Module Level 1 的首个公开工作草案,第一次在 CSS 层给外链资源开了一个传变量的口子。Chrome 的支持提案也在推进中(issues.chromium.org/issues/41482962)。

param() 的三种传参方式

外链 SVG 内部用 env() 接收变量,然后在 HTML 或 CSS 这一侧用三种方式之一把值”穿墙”传进去:

第一种,通过 link-parameters CSS 属性:

img.primary-icon {
  link-parameters: param(--theme-color, #6366f1);
}
img.danger-icon {
  link-parameters: param(--theme-color, #ef4444);
}

同一个 square.svg,两行 CSS,各自拿到不同的主题色。

第二种,直接拼在 URL 碎片里:

<img src="icon.svg#param(--theme-color, green)" alt="..." />

适合不想动 CSS、只想在 HTML 层面控制的场景。

第三种,塞进 url() 的参数里:

.btn-icon {
  background-image: url("icon.svg", param(--theme-color, currentColor));
}

hover 时直接改 link-parameters 的值就能切色,不需要换图片地址,也不需要 JavaScript。

外链 SVG 内部怎么接

SVG 这一侧不用改任何逻辑,只需要在属性值里写 env() 代替硬编码:

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path fill="env(--theme-color, #000)"
        d="M12 2L2 7l10 5 10-5-10-5z"/>
</svg>

env() 的第二个参数是兜底值,浏览器不认识 link-parameters 时会 fallback 到这个颜色,不至于白屏。整个 SVG 文件可以存一份,用在任何颜色主题下。

这件事为什么以前做不到

外链资源(img 的 src、background-image 的 url())和当前页面是分离的上下文,CSS 变量不跨域传递,这是浏览器安全模型的一部分。以前的解法都是在绕这个限制:内联 SVG 牺牲缓存、复制文件浪费磁盘、JS 注入徒增运行时开销。

link-parameters 本质上是在 URL 和 CSS 两条通道之外,给外链资源开了一条受控的样式注入路径。参数以 key-value 形式传递,SVG 内部用 env() 读取,浏览器的隐私边界没有打破,但前端工程化的一个十年痛点被解了。

现在能怎么用

这是一个还在草案阶段的功能,具体时间表取决于浏览器实现进度。但写到这里值得交代一下现状,免得你看完立刻去写生产代码然后骂我——Chrome 的 issue 已经开出去了,团队正在评估,这是好事。

现在的实际策略:

  1. 存量 SVG 不用动——给新引入的系统级图标加上 env() 接收参数就够了,渐进增强
  2. 设计系统里的高频图标优先改造——一个图标在不同按钮颜色下显示不同色的场景,现在靠复制文件解决,以后一份 + link-parameters 就够了
  3. 别为了用它而用它——如果你的 SVG 只有一种颜色,加这套机制是负优化

同一张图纸,同一个文件地址,页面里想让它是什么颜色,它就是什么颜色——这件事 CSS 终于开始认真对待了。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 11 阅读