你以为 CSS 只能做加减乘除?今天 round/mod/rem 把这件事彻底原生化了
CSS 的 calc() 解决了「能算」的问题,但「算出结果之后怎么取整」,一直是个悬而未决的工程难题——直到 round()、mod()、rem() 三个函数进入 Baseline 2024。
流体字体算出来 23.7px,设计师说最好给我整数。怎么办?
以前你得在 JS 里算完再 setProperty 回去,或者写一堆 @media 分段覆盖。现在三行 CSS:
h1 {
font-size: round(nearest, clamp(16px, 2.5vw, 48px), 1px);
}
clamp() 先算出流体值,round() 再把它取整到最近的整像素。字永远不会有 23.7px 这种尴尬的数字,亚像素渲染模糊从根本上消失。这是 round() 的最近取整策略(nearest),往上取整是 up(对应 JS 的 Math.ceil),往下是 down(对应 Math.floor),向零取整是 to-zero(对应 Math.trunc)。
网格步长对齐,是另一个高频场景。
设计师说「所有卡片的宽度必须是 8 的倍数」,你以前得在 JS 里先算好再塞进 CSS 变量。现在直接在 calc() 里套 round():
:root { --card-width: 87px; }
.card { width: round(var(--card-width), 8px); }
87px → 88px,自动对齐到最近的 8 的倍数。整个步长规则都在 CSS 里,不需要任何 JS 参与。
rem() 和 mod() 是什么?它们和 JS 的 % 运算符有什么关系?
margin: rem(18px, 5px); /* → 3px */
margin: mod(18px, 5px); /* → 3px */
对于正数,两者结果一样。区别在负数场景:rem(-18px, 5px) 返回 -3px(取被除数符号),mod(-18px, 5px) 返回 2px(取除数符号)。这和 JavaScript % 运算符取被除数符号是一致的,而 mod() 则像 Python 的 %。
实用场景:日历网格排版、进度条分段、Zebra 斑马纹动态计算:
.item {
--nth: 3;
/* nth 为 0 时表示偶数行 */
background: hsl(calc(mod(var(--nth), 2) * 180deg), 60%, 90%);
}
nth 由 JS 动态更新,背景色根据模运算结果自动切换,不再需要 nth-child 伪类匹配。
三个需要注意的坑
第一,round() 的步长参数至少有一个必须是 CSS 自定义属性。如果两个都写死,那直接写整数就行了,没有意义:
/* ❌ 这样写等于没写 */
width: round(100px, 25px);
/* ✅ 步长来自变量,这样才有意义 */
width: round(var(--fluid-width), 25px);
第二,rem()/mod() 不能混用单位,但可以混用数字和百分比——只要类型一致:
/* ✅ 同一类型 */
margin: rem(10%, 3%); /* → 1% */
/* ❌ 类型不一致,报错 */
margin: rem(10px, 3%);
第三,这些函数只管「算」,不管「布局约束」。它们不会改变容器的实际可用空间,只是在当前布局空间内做数学运算。如果你想让容器「恰好」容纳整数个子元素,那需要配合 grid 的 auto-fill/auto-fit 来解。
三步下一步
第一步:打开 DevTools,在控制台跑 CSS.supports('round(nearest, 10px, 1px)') 确认浏览器支持。Chrome 125+/Firefox 118+/Safari 17.2+ 全部通过,Baseline 2024,放心用。
第二步:找一个流体字体场景,把 clamp() 套上 round(var(--size), 1px),把 23.7px 变成 24px。刷新页面,肉眼可见字体边缘更锐利。
第三步:找一个网格或步长对齐场景,用 round(var(--width), 8px) 替代手写的分段 @media,把 5 行的降级代码删成 0 行。
这三个函数加在一起,不过是一行 CSS。但从这一天起,「计算完再取整」这件事,再也不用绕道 JS 了。
评论区
登录后可评论。