CSS原生随机数:终于不用JavaScript了
你有没有想过,在CSS里生成一个随机数?
不是用JavaScript,不是用Sass,就是纯CSS。比如“给每个卡片一个随机背景色”,或者“让雪花随机飘落”。
听起来很简单对吧?但如果你试过,你就知道CSS做不到。
准确地说,以前做不到。
CSS的“原罪”:确定性
CSS是一门声明式和确定性的语言。这两个特性决定了它天生和“随机”八字不合。
声明式意味着你告诉浏览器“我要什么”,而不是“怎么做”。你写color: red,浏览器就渲染红色,不会给你蓝色或黄色。确定性意味着相同的输入永远产生相同的输出。width: 100px永远是100像素,不会今天100明天120。
这两个特性让CSS变得可预测、可靠。你理解了布局引擎的规则,就能预判任何时刻的样式表现。
这很好。
但如果你想做点“随机”的东西,比如让页面每次刷新都有不一样的视觉效果,CSS就无能为力了。
于是在过去20年里,前端开发者们用尽了各种hack手段,试图从这门确定性语言里“榨出”随机性。
第一代hack:伪随机,自欺欺人
最早的方案是用:nth-child()选择器或者CSS动画来模拟随机。
比如你可以用:nth-child(odd)给奇数行加一种颜色,:nth-child(even)给偶数行加另一种。或者更花哨一点,用:nth-child(3n+1)、:nth-child(5n+2)制造更复杂的“看起来随机”的图案。
动画也是个思路。做一个复杂的多步骤动画,让元素的位移、旋转、缩放看起来毫无规律。
但这些方案本质上都是模式,不是随机。结果永远一样,人类可能一时看不出规律,但机器一定能预测下一个值。
自欺欺人,聊胜于无。
第二代hack:预处理器,编译时冻结
然后我们转向了工具链。Sass、SCSS、Less这些CSS预处理器提供了random()函数,终于能在编译时生成随机值了。
$random-color: rgb(random(255), random(255), random(255));
.card { background: $random-color; }看到这段代码你可能觉得“这不就解决了吗?”
问题在于“编译时”这三个字。
Sass的random()在编译阶段执行。编译完成后,随机值就永远冻结了,就像琥珀里的蚊子——生成的时候是活的,之后就定格了。
每次用户访问页面、刷新页面,看到的都是同一个“随机”值。要想产生新的随机值,你得重新编译一次样式表。
从“每次刷新都不一样”的需求来看,这还差得远。
第三代hack:服务端注入
下一步是把随机逻辑搬到服务端。PHP、Java、ASP这些服务端语言可以在生成HTML的时候注入随机值:
<?php $r = rand(0, 255); ?>
<div style="--random-hue: <?= $r ?>deg">这下好多了。每次页面请求都会生成新的随机值,用户每次刷新都能看到不同的效果。
但也有局限:如果页面上有动态添加的内容(比如用户交互后新出现的元素),这些新元素还是只能用初始加载时生成的“冻结”值。
而且随着单页应用(SPA)的普及,页面不再每次都完整刷新,服务端注入的方案就越来越力不从心了。
第四代hack:JavaScript,终于能随机了
最终我们把随机逻辑搬到了JavaScript。
用框架、CSS-in-JS库,或者原生JS,终于实现了真正的样式随机化:页面创建时随机、刷新时随机、DOM变更时也随机。
// 终于,真正的随机
element.style.setProperty('--hue', Math.random() * 360);JavaScript终于解决了这个问题。
但总觉得哪里不对。
用命令式方案解决声明式问题
那种“不对劲”的感觉来自两个层面:
第一,我们用命令式方案解决了一个声明式问题。
CSS负责“是什么”(声明式),JavaScript负责“怎么做”(命令式)。通过JS来做样式随机化,本质上是用“怎么做”来回答“是什么”的问题。
能用,但不是正道。
第二,我们把布局决策从CSS搬到了JavaScript。
CSS管布局,JavaScript管逻辑。为了绕过CSS的确定性限制,我们把样式决策塞进了JS里。技术上没问题,但架构上总觉得不干净。
每一代方案都在进步,但每一代都在用“更强的工具”来解决一个“本不该这么难”的问题。
CSS的回答:random()和random-item()
2026年,CSS Values and Units Module Level 5终于给出了原生答案。(这是W3C CSS工作组制定的CSS值和单位规范的第5版,目前处于草案阶段,定义了CSS属性值的各种数据类型和计算方式。)两个新函数:
/* random() — 生成min到max之间的随机值 */
.card {
background: hsl(random(0, 360) 70% 60%);
}
/* random-item() — 从列表中随机选择一个 */
.badge {
color: random-item(red, blue, green, orange);
}就这么简单。没有工具链,没有服务端,没有JavaScript。CSS自己就能随机了。
你可以在任何CSS属性里使用它们:
/* 随机旋转角度 */
.star {
transform: rotate(random(0, 360)deg);
}
/* 随机动画延迟 */
.particle {
animation-delay: random(0, 5)s;
}
/* 随机位置 */
.snow {
left: random(0, 100)%;
animation-duration: random(3, 8)s;
}为什么这不只是“又一个新特性”
你可能会想:CSS加个随机函数,有什么好大惊小怪的?
因为这代表了CSS语言本质的转变。
W3C有一个原则叫Rule of Least Power(最小能力原则):解决问题时,应该选择能力最弱但足以胜任的语言。
在Web平台上,能力排序是:HTML(最弱)→ CSS → JavaScript(最强)。这个原则建议:能用CSS解决的问题,不要交给JavaScript。
为什么?因为“更弱”的语言往往更适合特定任务。它的特性会针对该层级的需求做优化,更简单、更高效、性能更好。更强的语言当然也能做,但会引入不必要的复杂性。
过去20年,CSS的随机性问题一直被“外包”给JavaScript。这违反了最小能力原则——我们用了一个更强的语言来做一件本该在样式层解决的事。
现在random()把这个问题拉回了它该在的位置。
代价和边界
说了这么多好话,也得说说现实。
浏览器兼容性:这是Level 5规范的新特性,目前浏览器支持还在推进中。在生产环境使用前,一定要查Can I Use。
规范可能变化:Level 5还是草案阶段,API细节可能会调整。这在Web标准演进中很常见——想想legacyBehavior的教训,曾经的“正确用法”后来变成了“遗留行为”。
渐进增强:如果你现在就想用,考虑用@supports做渐进增强:
.card {
/* 降级方案 */
background: hsl(200 70% 60%);
}
@supports (background: hsl(random(0, 360) 70% 60%)) {
.card {
background: hsl(random(0, 360) 70% 60%);
}
}这意味着什么?
CSS原生随机性不只是“又一个API”。它标志着CSS从一门纯粹的样式语言,向生成式布局系统的方向迈进了一步。
以前CSS是被动的——你告诉它什么,它就渲染什么。现在它有了“主动”的能力——它可以自己决定“这次渲染什么”。
这打开了很多可能性:生成式布局、有机纹理、个性化微交互、有生命感的设计系统。
当然,现在说这些还为时尚早。浏览器支持、规范稳定性、实际性能这些都需要时间验证。
但方向是清楚的:CSS不再只是一门“描述性”语言,它正在成为一门“生成性”语言。
这20年的hack历史,终于画上了一个句号。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!