核心一句话

即使 height = line-height = font-size,字体本身自带上下边距(字体的 ascent / descent,上升区、下降区),不是文字可视内容刚好塞满 font-size,所以依然可能顶部被切。
很多人误区:font-size = 文字图案高度。❌
font-size 只是字体设计的em正方形,不是字的可见笔画高度。
- 字体内部结构(根源)
每个字体文件内置:
• ascent:基线往上的最大高度
• descent:基线往下的最大高度
ascent + descent 经常 大于 font-size!
浏览器计算行盒默认逻辑(line-height:normal 时)就依赖这两个值。
哪怕你手动设置 line-height: 20px; font-size:20px; height:20px
• 行盒是20px
• 但字体字形的可视笔画顶部,会超出这个20px行盒的上边界
• 再加上容器高度也是20px → 顶部笔画就被裁掉(尤其带上升笔画:如"天、国"的顶部横,英文b/d/h)
不同字体差别巨大:思源黑体、微软雅黑、苹方的 ascent 值不一样,有的字体留白在上,有的在下。
- 基线居中的问题(单行文字)
line-height 的计算:
行高 = 上下半行距 + font-size
半行距 = (line-height - font-size) / 2
当 line-height === font-size → 半行距 = 0。
行盒高度等于font-size,没有额外上下留白。
但字体的可视图形,本身就会侵入到行盒边界外面:
• 部分字体的 ascent 很大,文字笔画顶到行盒上边甚至超出
• 容器高度等于行盒高度,overflow:hidden 时直接切掉超出的顶部
- 为什么有的字体没事、有的会挡?
举直观例子:
• 微软雅黑:ascent 偏大,容易顶部裁切
• 思源黑体常规:相对克制,不容易裁
这就是同一个css,换个字体就出现文字被切的经典坑!
- 还有一个叠加坑:box-sizing + 边框/padding
.box{
font-size:20px;
line-height:20px;
height:20px;
border:1px solid #000;
}
默认 content-box:height 是内容区20px,加上上下边框2px,内容区域被压缩,直接裁切文字。
很多人忘记边框,导致内容高度变小,文字顶部被挡。
-
解决方案(常用)
-
行高稍微大于字体大小,预留安全边距
font-size:20px;
line-height: 24px; /* 留一点点上下留白 */
height:24px;
-
用 padding 代替固定 height 做垂直居中(推荐单行文字)
font-size:20px;
line-height:1.2;
padding:4px 0;
height:auto;
-
或者 vertical-align 调整基线,但是治标不治本
-
现代方案:flex 居中,摆脱 line-height 坑
.box{
display:flex;
align-items:center;
height:20px;
}
总结
height = line-height = font-size 只是css盒子层面相等;字体本身的字形绘制区域不一定被限制在font-size方框内,不同字体的 ascent 不同,部分字体顶部笔画会超出,overflow:hidden就会出现顶部遮挡。
一、基线 baseline
基线是字体排版的虚拟参考线,不是下划线。英文小写字母 x 的底部就是基线。
b ↑ ascent(上升区:基线往上)
o
x --------- baseline【基线】
g ↓ descent(下降区:基线往下,g/j/q 的尾巴)
• ascent:基线向上,字体能到达的最高点
• descent:基线向下,字体能到达的最低点
字体文件内置这两个值,和 CSS 的 font-size 相关,但不等于 font-size
重点:font-size 是字体的 EM 正方形尺寸,只是设计网格,不是字形可视高度。
很多字体 ascent + descent > font-size → 字形会伸出 EM 框外面,这就是裁切元凶。
二、line-height: normal 默认值
line-height: normal 不是固定数字!
计算公式:
line-height:normal ≈ font-size × 字体内置比例系数
系数写在字体文件里,不同字体不一样:
• 微软雅黑:系数大约 1.2
• 思源黑体:大约 1.1~1.2
• 宋体、一些英文字体可能 1.0~1.3
✅ 所以:
font-size:20px;
line-height:normal;
行高 ≈ 20px * 1.2 = 24px,不是20px!
误区:很多人以为 normal = 1,❌。line-height:1 是强制行高等于font-size,和normal完全两码事。
line-height:1 会把行盒压缩到EM框大小,最容易触发文字顶部/底部裁切。
行盒计算公式(line-height 固定数值时)
半行距 = (line-height − font-size) / 2
半行距会平分加到 font-size 的上下,构成行盒。
注意:半行距是加到字体EM框上下,不是加到字形上下。
当 line-height = font-size,半行距=0,没有额外留白。此时字体本身的 ascent 一旦超出 EM 框,直接溢出。
三、如何防止文字被遮挡(方案按推荐优先级排序)
✅ 方案1:抛弃 height + line-height 居中,改用 flex(最稳,推荐)
完全绕开基线、ascent、行高各种坑
.box {
height: 32px;
display: flex;
align-items: center;
overflow: visible; /* 不要hidden! */
}
flex 的 align-items:center 不是基于基线排版,不会被字体ascent差异影响。
✅ 方案2:不写固定height,用padding撑开(单行/多行都友好)
.box {
font-size:20px;
line-height:1.4;
padding:4px 8px;
height:auto; /* 高度自动由文字+padding决定 */
}
适合按钮、标签,完全杜绝裁切。
✅ 方案3:坚持使用固定height + line-height(老项目兼容)
不要 line-height = font-size!
预留安全余量:
.box{
font-size:20px;
line-height: 24px; /* 比font-size大一点,预留字体ascent溢出空间 /
height:24px;
overflow:visible; / 尽量不要overflow:hidden,万不得已才用 */
}
经验值:中文界面 line-height 一般取 1.2 ~ 1.4,避开 1.0。
✅ 方案4:必须 overflow:hidden 的场景(如裁切容器需求)
• line-height 适当放大,给上下预留安全边距
• 尽量测试目标字体(微软雅黑最容易顶部裁字)
• 可以微调 vertical-align 做临时偏移,但治标不治本
❌ 不推荐写法(极易裁切)
.box{
font-size:20px;
line-height:20px;
height:20px;
overflow:hidden;
}
line-height:20px 等价系数1,半行距0,只要字体ascent超出EM框,顶部直接被切掉。
四、一句话总结
-
基线:小写x底部,ascent向上、descent向下,字体字形本身可能超出font-size方框。
-
line-height:normal:由字体内置系数 × font-size,不同字体不一样,不等于1。line-height:1 才是行高=font-size,风险最大。
-
防遮挡最优解:flex居中 / padding自适应高度;迫不得已固定高度时,line-height > font-size,避免 line-height=font-size + overflow:hidden
需要我写一段完整demo,对比 line-height:1 和 line-height:normal 同时看裁切效果吗?