一个同事提交了一个PR,标题是"修复小屏设备上的标题显示问题"。改动内容是在样式文件里加了一条媒体查询。我审查代码时发现,这个文件里已经有四条类似的媒体查询了——对应四个不同的断点,由四个不同的人在十八个月里陆续加上去的。每个人都在修补上一个人没想到的屏幕宽度。
代码长这样:标题的字体大小被拆成了五条规则,每条规则对应一个断点范围。从3rem到1.5rem,中间隔着1200px、992px、768px、480px四个断点。看起来覆盖了所有常见屏幕尺寸,但实际效果并不理想。
![]()
把窗口拖到850px宽,标题会卡在992px那一档的数值上——2.25rem,对当前实际空间来说偏大了。每个断点之间的空隙,都是一个没人认真选过的尺寸,只是离它最近的规则留下的默认值。
真正让人无奈的是:从2020年开始,这些代码就完全没有必要了。
clamp():一条规则替代五条
CSS的clamp()函数接收三个参数——最小值、首选值、最大值——然后根据当前情况返回其中一个。用在这个场景里,写法是这样的:
font-size: clamp(1.5rem, 1rem + 2vw, 3rem);
翻译成人话:永远不小于1.5rem,永远不大于3rem,在这两者之间,随视口宽度连续缩放。上面那五条媒体查询,被这一行代码全部替代。而且和媒体查询不同,clamp()没有"空隙"问题——它会在视口移动的每一个像素上重新计算数值,不存在"850px时该用哪个值"的尴尬。它是一个公式,不是一个查表。
中间那个参数是"首选值"所在的位置,写法是1rem + 2vw——一个固定部分加上一个视口相对部分——而不是单纯写4vw。这个细节不是装饰,它是整个模式里最值得搞对的部分,因为偷懒的写法会悄悄破坏一些东西。
4vw的陷阱:默认字号设置失效
网上半数相关博客文章里出现的公式是这样的:
font-size: clamp(1.5rem, 4vw, 3rem);
更简洁,视觉上缩放效果也完全一样。在浏览器里试一下,看不出任何区别——直到你进入浏览器设置,把默认字号调大,就像低视力读者经常做的那样。只用了4vw的标题纹丝不动。不是稍微动一点,是完全不动。
原因在于:vw是视口宽度的百分比,和页面根元素的字号没有任何关系。用户设置的默认字号偏好——这是一个真实的辅助功能设置,不是边缘情况——在vw这里找不到任何可以依附的东西。而rem有关系:它相对于根元素的字号,正好就是那个设置会改变的东西。在首选值里混入rem项,标题就会随用户偏好一起变大。去掉rem项,你构建的标题就对用户的辅助功能设置完全免疫了。
为什么1rem + 2vw比4vw更合理
把首选值写成1rem + 2vw,本质上是在说:标题的基础大小跟随用户的默认字号偏好,缩放部分跟随视口宽度。两个维度各司其职,互不干扰。
只写4vw的话,整个值都挂在视口宽度上。用户的字号偏好被完全忽略,缩放比例也被迫承担了本不该由它独自承担的全部工作。
这个区别在视觉上几乎看不出来,但在辅助功能层面是实质性的差异。对低视力用户来说,浏览器默认字号的调整是他们浏览网页的基本方式。一个响应这个设置的标题,和一个完全无视它的标题,体验差距是真实存在的。
媒体查询还有用吗?
当然有。媒体查询适合处理布局层面的变化——比如侧边栏在小屏上折叠、导航从横向变成汉堡菜单、网格从四列变成一列。这些是结构性的跳变,需要明确的断点来触发。
但字体大小不是这种场景。字号需要的是连续变化,不是跳变。用媒体查询做连续变化,本质上是在用离散的阶梯去逼近一条连续的曲线,结果就是那些没人选过的中间值。
下次再遇到"修复标题在小屏上的显示"这类PR,先看看文件里是不是已经堆了好几层媒体查询。如果是,删掉它们,换成一行clamp()。代码更少,行为更连续,还顺手解决了辅助功能的问题。
从2020年开始,这件事就已经不需要纠结了。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.