CSS 函数 · 值是「算」出来的
CSS 属性值远不止 20px、red 这样的字面量 —— 一大票值长成 name(参数) 的样子: calc(100% - 80px)、clamp(16px, 4vw, 40px)、oklch(0.7 0.15 50)、linear-gradient(red, blue)……这种写法叫 functional notation (函数式记法): 函数名 + 括号里的参数, 浏览器拿参数算出最终的值。它们不是零散的语法糖, MDN 把全部 value function 归成了 14 类 115 个的一张完整地图。
本系列先用一页全景图认全这 14 类 (用 CSS.supports 实时检测当前浏览器支持哪些); 再深入其中最常用也最新的 math functions: calc() 与 min/max/clamp 的混单位算术、round()/mod()/rem() 的取整策略与负数行为、把任意值线性映射到 0–1 的 progress()、为 CSS 带来原生随机数的 random()、以及 tan(atan2()) —— 用一对三角函数把带单位的长度「类型转换」为纯 number。全程滑块实时重算 + 真实 getComputedStyle 读数。最后把视角反过来: @function at-rule 让你自己定义带参数 / 类型 / 默认值的 CSS 函数。
前置: random() 的「共享语义」涉及 var() 是文本代换这一机制, 需要时可参考 custom-properties 系列。
函数全景图:你的浏览器认识几个
照 MDN 的分类法把全部 value function 摆成一张地图:transform / math / filter / color / image / counter / shape / reference / grid / font / easing / animation / anchor / tree counting 十四类。每个函数一枚 chip,用 CSS.supports() 实时检测当前浏览器是否支持——绿色支持、红色不支持;点 chip 查看对应的最小探针。换个浏览器打开,这页呈现的结果不同,这正是 Baseline 要解决的问题。
calc() 混单位算术与 clamp() 流体值
calc() 的核心能力是混合单位运算: 100% - 80px 在写下时还无法求值,浏览器到 layout 时才代入当下的 100%。再看比较三函数:min() 取最小、max() 取最大、clamp(MIN, 理想值, MAX) 把理想值夹在区间内——流体字号一行即可实现。拖动「视口宽」滑块,在数轴上看 clamp 三个候选值中谁生效、字号如何随之变化。
round() 的四种取整策略,以及 mod 与 rem 的区别
round(策略, 值, 步距) 把值取整到步距的整数倍,四种策略 up / down / nearest / to-zero 恰好对应 JS 的 ceil / floor / round / trunc——差异全在负数上:up 朝 +∞、to-zero 朝零,在数轴上一目了然。再看 mod() 与 rem(): 都是求余,余数的符号取决于谁不同(mod 跟除数、rem 跟被除数,JS 的 % 属于 rem)。附 8px 网格吸附示例与 round(10px, 0) 这类特殊值的实测。
progress(): 把任意值线性映射到 0–1
progress(value, start, end) 计算的就是 (value - start) / (end - start),再夹到 [0, 1]——一个标准的归一化函数。得到 0–1 的数后,乘以什么就插值什么:透明度、色相、圆角。重点在它接受 length: progress(100vw, 320px, 1200px) 这种「长度除长度」用 calc 写不出来(单位约不掉), progress() 内部负责约分。附在所有浏览器都能运行的等价写法与实测对照。
random(): CSS 原生掷骰子
random(min, max, step?) 为 CSS 带来原生随机数——绘制一片星空无需任何 JS。最需要厘清的是「随机值由谁共享」: 默认每处使用各自取值;--ident 让同一元素内多个属性共用同一个值(用 var() 存储无效,因为 var 是文本代换、每处代换各取一次); element-shared 让所有元素共享;两者叠加 = 全局一份。2×2 矩阵把四种语义并列对照,星空 demo 在支持的浏览器使用真实 CSS、否则用 JS 模拟并明确标注。
tan(atan2()): 把长度转换成 number
calc() 不允许「长度除长度」,但 atan2(A, B) 接受两个同量纲的值、内部只取比值——单位随即约掉;tan() 再把角还原成比值:tan(atan2(A, B)) = A / B,一对三角函数充当单位消除器。三个示例全程零 JS: 100vw 读成数字(@property 先定形成 px)、容器宽折合多少 em、12s 换算成 12001ms——数字由 counter() 打印。末尾把混单位 / 裸写不包 calc / 除数为 0 / 大比值精度等历史问题列成一张表,实时检测你的浏览器表现。
alpha(): 只改透明度的颜色函数
alpha(from <color> / 值) 吃一个 origin color, 三个色度通道原样保留,只把透明度替换或缩放——它是 relative color 语法 (rgb(from c r g b / alpha)) 的单通道便捷形式,不必把 r g b 整套抄一遍。斜杠后既能给常量(/ 80% 直接覆盖),也能借 alpha 关键字(origin 透明度的 <number>)进 calc: calc(alpha * .25) = 在原透明度上再打到四分之一。出自 CSS Color 5(草案),支持 alpha() 的浏览器跑真函数、其余自动退回等价的 relative color 展开形式并标注。
@function: 定义你自己的 CSS 函数
前面几页讲的都是浏览器内置的函数——只能用、不能造。@function at-rule 反过来:author 自己定义带参数、<length> / <color> 等类型约束、默认值与 returns 返回类型的可复用函数,内部还能写局部变量和逻辑,用 result 吐值。它不是 var() 那种文本代换,而是真正的求值。两个 demo(调浅颜色的 --tint、封装流体字号的 --fluid)在 Chromium 139+ 跑真 @function,其余浏览器退回 JS 模拟并标注。
param(): 把 CSS 变量传进被引用的图片里
其余函数都是「把输入算成一个值」,param() 是个例外:它不计算,而是在引用一张图片的地方(<img> 的 URL 片段、link-parameters 属性、或 url() 内)往那张图塞进一个具名参数;图片内部(通常是 SVG)再用 env(--x, fallback) 把它接出来。解决的痛点是 <img> / background-image 引入的外部 SVG 是隔离沙箱,外层 var() / currentColor 进不去——param() 给这道墙开一扇单向具名传参口,一份资源即可随调用方换色。出自 CSS Linked Parameters 草案,尚无浏览器实现,全程 JS 模拟 env() 代换并标注。
🔗 相关链接
📐 规范 / 文档
-
CSS Values and Units Module Level 4 · Mathematical Expressions
drafts.csswg.org
calc()的算术、min/max/clamp比较、round/mod/rem取整、三角与指数函数都定义在这一节。 -
CSS Values and Units Module Level 5
drafts.csswg.org
progress()、random()、calc-mix()等较新的值函数所在的模块 (editor's draft)。 - CSS value functions developer.mozilla.org 本系列分类法的出处: 14 类 value function 的完整索引。
- Can I use · CSS math functions min(), max() and clamp() caniuse.com 比较三函数的实时浏览器支持矩阵。
-
Can I use · calc() as CSS unit value
caniuse.com
calc()的实时浏览器支持矩阵。
各页的主要来源放在对应子页末尾的「相关链接」里。下面是贯穿全系列的总参考:
- Rolling the dice with CSS random() webkit.org WebKit 团队介绍 random() 的设计与用法: 星空、照片堆、抽奖转盘。random() 页的主要来源。