跟着页面进入深色模式的计数器
计数器是别人页面上的一张图片。当那个页面有深色模式时,图片并不知道:它是在服务器上画好的,那时访客的系统偏好甚至还不是一个事实。深色页面上一枚白色徽章是常见结果,看起来像个洞。
这里有十五个样式照样能应付。不是去问页面,也不靠 JavaScript——一行都没有参与。图片自己就切换了。
怎么做到的
SVG 不是图片,而是文档,而文档可以带一份样式表。所以服务器把样式画两遍,浅色和深色,把两份放进同一个文件,再加三行 CSS:
.s4uD{display:none} @media(prefers-color-scheme:dark){.s4uL{display:none}.s4uD{display:inline}}
浏览器会在图片内部套用这条媒体查询,哪怕这张图片是通过一个普通的 <img> 标签从另一个域名加载来的。把系统切到深色,计数器就跟着走——不用重新加载,周围那张页面全程没有参与。
代价是多少
两个版本都在文件里,所以文件大约是两倍大。在一个样式上实测:平常 1 029 字节,带上深色双胞胎是 2 041 字节。这是诚实的价格,也是它不作为默认项的原因:它是在嵌入时按需索取的,而不是强加给所有人。
没有第二次请求,也没有第二个往返。来的是一个文件,用不上的那一半永远不会被绘制。
中间的那个陷阱
SVG 里的渐变、滤镜和蒙版是靠 id 引用的。一个样式的两个版本出自同一份代码,所以两边都带着叫 g1、g2 之类的渐变。把两边放进同一个文档,某个 id 的第二次定义就干脆不生效——深色版本会悄无声息地用浅色版本的颜色画出来。
没有任何东西报错。文件是合法的。它只是看着不对,而且这种不对很容易被算到样式头上,而不是算到拼装头上。
所以在两半合并之前,深色版本里的每一个 id 都被加上前缀重命名,指向它们的每一个 url(#...) 也随之改写。这是很小的一步,跳过它会造出一个匆匆一瞥无法解释的缺陷。
它什么时候拒绝
如果缺了任何一半,如果有一半没产出 <svg> 元素,或者两半逐字节完全一样——那意味着这个样式没有深色变体——就原样返回浅色版本。一个拼到一半的 SVG 比完全没有深色模式更糟,而一个能画出来的计数器,比一个聪明的计数器更值钱。