CSS文本框上下自带留白,text-box-trim和text-box-edge咋整?

2,715字
11–17 分钟
in

写页面时常常遇到这种破事:明明给标题设了margin-bottom: 50px,结果肉眼看上去像是有八九十像素,怎么调都差那么点意思。这背后黑手就是字体自带的“行间距”上下半套——专业点叫leading,它像一层透明护垫,贴在每行文字的上方和下方,保证多行文本阅读时不挤在一起。但单行文本框或者大标题也带上这玩意儿,就会把原本设计的间距全部搞乱。text-box-trimtext-box-edge这俩新兄弟就是专门来收拾这个烂摊子的,前者负责“剪掉”多余空白,后者决定“从哪个位置开始剪”。

目录

啥是leading

拿一个装了水的杯子打比方,水是文字,杯壁和水平面之间的空气就是leading。水倒进去之后,水面离杯口总有一段空档,这段空档在CSS里就是每个文本盒子上下自带的留白。不同字体、不同字号,这段空档的厚度都不一样。比如Arial字体下,大写字母“H”顶部离盒子边缘有8px空白,但换成Fraunces字体可能就变成12px。这意味着写样式时没法靠死数字一次搞定,改个字体就得重新算一遍间距,烦得一批。

修剪上下留白

实际动手操作前,先得知道这俩属性的浏览器支持有点翻车——Chrome 128+和Safari 16.4+要手动开实验开关,Safari TP倒是默认开着。开发时最好先测一下,免得上线后炸了。

方案一:模仿Figma垂直修剪(首尾都剪)

这个方案适合按钮、卡片标题这类上下都需要贴边的场景,效果跟Figma里“垂直修剪”的“Cap height to baseline”选项一模一样。

  1. 选中需要修剪的文本盒子,比如一个<h1>大标题
  2. 设置text-box-trim: trim-both;,意思是头尾两端的空白都砍掉
  3. 设置text-box-edge: cap alphabetic;,第一个值cap告诉浏览器:头顶空白从大写字母顶部开始算(比x-height更靠上,适合英文标题),第二个值alphabetic告诉浏览器:脚底空白从字母基线开始砍(不保留下行字母的额外空间)
h1 {
  margin-bottom: 50px;   /* 想要的实际间距 */
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}

放一个实际例子:原本margin-bottom: 50px加上leading送的37px,总间距87px,看着像得了水肿。套上这俩属性后,37px被剪干净,50px就是实打实的50px。这里有个坑:老版本浏览器可能认trim-bothboth,甚至更老的认topbottom。如果发现没效果,可以尝试把text-box-trim: both;或者leading-trim: both;(老属性名)轮着测一遍。

方案二:只剪头顶(保留脚底空白)

有时候只想把标题上方的多余空白干掉,但下方要留着一点空间给下标或者特殊符号,那就只剪开头。

  1. 定位到目标元素,比如侧边栏的章节标题
  2. text-box-trim: trim-start;,单剪上方
  3. text-box-edge: ex;ex是指x-height(小写字母x的高度),这个值会砍掉从x-height往上到盒子边缘的空白,同时因为没指定第二个值,浏览器默认给补个text(即脚底按字体的升部/降部来,不剪)
.sidebar h2 {
  margin-top: 30px;
  text-box-trim: trim-start;
  text-box-edge: ex;   /* 相当于 ex text */
}

注意trim-start配合text-box-edge时,第一个值(头顶)必须填一个合法值,哪怕剪的是头顶也得填。如果只写text-box-edge: alphabetic会翻车,因为alphabetic不能当头顶值用。正确姿势是随便塞个excap,比如text-box-edge: cap alphabetic;,然后靠trim-start把头顶剪掉,脚底alphabetic其实没起作用但语法没错。

方案三:只剪脚底(保留头顶空白)

这个稍微绕一点,因为CSS语法强制要求先写头顶值再写脚底值,哪怕头顶不剪也得给个值占位。

  1. 找到需要剪脚底的元素,比如段落最后一行贴边的情况
  2. text-box-trim: trim-end;
  3. text-box-edge: cap alphabetic;——头顶随便塞个cap(虽然不剪,但必须存在),脚底用alphabetic砍掉基线以下的空白
p:last-child {
  margin-bottom: 20px;
  text-box-trim: trim-end;
  text-box-edge: cap alphabetic;
}

这个方案的坑在于很多人会写成text-box-edge: alphabetic;然后报错。记住一个口诀:头顶值永远不能缺席,哪怕trim-end让你觉得头顶用不着,也得硬塞一个。

常用修剪组合速查表

组合值修剪效果适用场景
trim-both, cap alphabetic砍头顶+砍脚底(基线)按钮、卡片标题
trim-start, ex只砍头顶(x高)侧边栏小标题
trim-end, cap alphabetic只砍脚底(基线)段落末行贴容器底
trim-both, ex alphabetic砍头顶(x高)+砍脚底正文区域多行文本

偷懒用text-box缩写

嫌两个属性写起来手酸?可以用text-box这个缩写,顺序是:先写修剪模式,再写头顶值,最后写脚底值。

/* 完整写法 */
text-box: trim-both cap alphabetic;

/* 省略脚底值(默认text) */
text-box: trim-start ex;   /* 相当于 trim-start ex text */

/* 只剪脚底,头顶占位cap */
text-box: trim-end cap alphabetic;

Chrome里目前有个迷之行为:text-box-edgeauto值必须单独使用,不能跟其他值混搭。比如想写text-box-edge: auto alphabetic会直接失效。这问题官方还没修,碰到需要动态判断的复杂场景,建议还是分开写两个属性先凑合用。

字体差异踩坑实录

不同字体提供的升部(ascender)和降部(descender)数据差别巨大。Arial字体下,text值会连带把重音符号(比如é上面的小撇)也包进去,导致头顶留白比预想的多一丢丢。而Fraunces字体更离谱,它的降部居然包含了底部重音符号的空间。所以用text值时要心里有数——它其实等于“按字体设计师给的原始数据来剪”,很多时候并不符合实际视觉需求。如果发现修剪完上下依然不对秤,换成capex试试,这俩值不受字体元数据影响,更稳定。