做这个站的时候我翻出以前写的样式表,看到这么一段:
h1 { font-size: 32px; }
@media (min-width: 768px) { h1 { font-size: 44px; } }
@media (min-width: 1024px) { h1 { font-size: 52px; } }
@media (min-width: 1440px) { h1 { font-size: 58px; } }
四行代码管一个字号。问题是:在 1023px 到 1024px 之间,字号是跳变的。 窗口拉过去的那一下,标题会「咔」地一下变大。而且每加一个元素就要再抄一遍。
现在我用一行 clamp() 就解决,而且字号是连续变化的。
clamp() 是什么
它接受三个值:clamp(最小值, 首选值, 最大值)。
浏览器会算出首选值,然后把它夹在最小和最大之间——小于最小值就取最小值,大于最大值就取最大值。
关键在首选值怎么写:用 vw(视口宽度的百分之一)加上一个固定偏移。
比如 4vw + 16px,屏幕宽 1000px 时等于 40 + 16 = 56px,宽 500px 时等于 20 + 16 = 36px。
视口越宽字越大,但因为有固定那部分在,不会小到看不清。
本站实际在用的阶梯
这是我站上跑的一套字号,直接抄就行:
:root {
/* 正文:手机上 15px,桌面上 16px,中间平滑过渡 */
--fs-body: clamp(15px, 0.4vw + 14px, 16px);
/* 小字:日期、标签、版权 */
--fs-sm: clamp(12px, 0.2vw + 11.5px, 13px);
/* 段落标题 */
--fs-h2: clamp(22px, 1.6vw + 16px, 28px);
/* 页面主标题 */
--fs-h1: clamp(32px, 3.2vw + 16px, 46px);
/* 首屏大标题:变化幅度最大 */
--fs-hero: clamp(38px, 6vw, 62px);
}
实际用法就是一句 font-size: var(--fs-h1);。
好处是整套字号只在一个地方定义,想整体调大调小,改这里就够。
怎么定这三个数
我的做法是「先在两头看图,再倒推中间」。步骤:
- 先定最大值:把浏览器拉到最宽(比如 1600px),调到你觉得舒服的字号。比如 46px。
- 再定最小值:拉到 375px(iPhone 宽度),调到刚好不挤、不换行难看。比如 32px。
- 最后倒推首选值:让中间某一点的斜率自然过渡。粗略算法是
(最大 - 最小) / (最大视口 - 最小视口) × 100,得到 vw 的系数。
以 h1 为例:最大 46px 在 1600px,最小 32px 在 375px。
差值 14px,视口差 1225px,14 / 1225 × 100 ≈ 1.14vw。
再算偏移让两端对得上,得到大致 1.14vw + 27.7px。
实际用的时候不用这么精确,取整到 3.2vw + 16px 就够了——
反正有 clamp 兜着,不会超出范围。
不用追求数学精确。clamp 的意义是把「两端」锁死,中间怎么走都可以。
中文排版要多注意的一点
这套写法放在英文站上很舒服,搬到中文上会有一个问题:中文字面率比西文高。 同样 16px,中文看起来比英文更满、更「堵」。原因是汉字基本占满整个字身框,而拉丁字母有大量留白。
所以中文站要额外做两件事:
- 行高要放宽。英文正文 1.5 够了,中文我会放到 1.75 到 2.0。这个站正文用的是 1.75,长文章用 2.0。
- 最小字号别低于 14px。中文在 12px 以下笔画会糊成一团,尤其是宋体类的标题字。英文 12px 还能看清,中文不行。
还有个细节:中文正文不要用衬线体(宋体类)之外的花哨字体。
这个站的标题用了宋体(Songti SC / SimSun),正文用系统无衬线,
就是为了保证小字号下的可读性。
兼容性
clamp() 从 2020 年起主流浏览器就全支持了,现在没有兼容问题。
唯一要注意的是:如果你的站点还要支持很老的浏览器,得在前面写一句降级:
h1 {
font-size: 36px; /* 老浏览器兜底 */
font-size: clamp(32px, 3.2vw + 16px, 46px);
}
浏览器不认识 clamp() 会忽略第二行,用第一行。认识就用第二行覆盖掉。
这个技巧叫「渐进增强」,写 CSS 时挺常用。
一个反例
别在 clamp() 里塞 calc() 嵌套复杂的分段函数,比如
clamp(14px, calc((100vw - 400px) * 0.5), 22px)。
能跑,但没有任何人能看懂,三个月后你自己也看不懂。
响应式字号的目的是让字在各种屏幕上都能读,不是炫技。两三个变量解决问题,就别写五个。