写过 CSS 的人都踩过这个坑——动画要 stagger、宽度要均分、颜色要平铺,以前全靠 JS 算 index 注入 custom property。今天两个 CSS 函数把这件事彻底原生化了
写过 CSS 的人都踩过这个坑——动画要 stagger、宽度要均分、颜色要平铺,以前全靠 JS 算 index 注入 custom property。今天两个 CSS 函数把这件事彻底原生化了
以前怎么做的
动画 stagger:5 个卡片错开 80ms 入场,传统做法:
items.forEach((item, i) => {
item.style.setProperty(--delay, `${i * 80}ms`);
});
.card { animation-delay: var(--delay, 0ms); }
等宽 Grid:不知道有几个 tab,全靠后端渲染或者 JS 探测 children 数量再动态算宽度。
色彩平铺:N 张卡片从左到右色相均匀分布,以前要么硬编码要么 JS 循环注入 hsl 值。
根本问题是:浏览器知道这些信息,但 CSS 拿不到。:nth-child 只是个选择器,它能选中元素,但它的值不能参与 calc() 运算。
两个新函数
Chrome 138+ / Safari 26.2+ / Edge 138+ 稳定支持:
sibling-index() — 元素在其父级 children 中的位置(从 1 开始)
sibling-count() — 父级共有多少个 element children
两者都是纯数值,可以直接塞进 calc():
/* 动画 stagger,一行搞定 */
.card {
animation: fade-in 0.4s ease both;
animation-delay: calc(sibling-index() * 80ms);
}
/* 等宽 Grid,插一个动态刷新一个 */
.tab {
width: calc(100% / sibling-count());
}
/* 色相均匀分布 */
.swatch {
background: hsl(calc(sibling-index() * 360deg / sibling-count()) 70% 50%);
}
关键点:DOM 变了,CSS 自动重新计算。插一个卡片进去,所有卡片的宽度、延迟、色相全部自动刷新,零 JS 干预。
sibling-index() 从 1 开始计数,不算 text node 和 comment。hidden 元素(display: none)是否计入各浏览器略有差异,写关键线上样式时建议实测。
实战场景
1. 交错入场动画
正向 stagger(先出现的后延迟):
.card {
animation: slide-up 0.4s ease both;
animation-delay: calc(sibling-index() * 80ms);
}
反向 stagger(最后出现的先入场):
.card {
animation: fade-in 0.4s ease both;
animation-delay: calc((sibling-count() - sibling-index()) * 80ms);
}
配合 prefers-reduced-motion 关闭:
@media (prefers-reduced-motion: reduce) {
.card { animation: none; }
}
2. 动态等宽 Grid
.grid { display: flex; flex-wrap: wrap; }
.col { flex: 1; min-width: calc(100% / 12); }
三个子元素各占 33.33%,四个各占 25%,删掉一个剩下三个自动重新分布——这个以前必须靠 JS resize observer 才能做到。
3. 色相平铺
.swatch {
background: hsl(
calc((360deg / sibling-count()) * sibling-index())
70%
50%
);
}
N 张卡片从左到右 Hue 均分,第一次实现纯 CSS 自动跟随数量变化。
4. 径向布局
结合 CSS 三角函数做圆形排列:
.radial-item {
--angle: calc((360deg / sibling-count()) * sibling-index());
left: calc(50% + 100px * cos(var(--angle)));
top: calc(50% + 100px * sin(var(--angle)));
}
避坑指南
1. Shadow DOM 边界
sibling-index() 和 sibling-count() 只看 immediate parent 的 direct children。Shadow DOM 内的元素只会计数 shadow tree 内的 sibling,不会穿透 ::slotted 看到 light DOM。
2. 不是替代 nth-child
:nth-child 是 selector,用来选元素;sibling-index() 是 value function,用来参与运算。用错场景会导致整条规则失效。
3. 支持检测
@supports (width: sibling-count()) {
.col { width: calc(100% / sibling-count()); }
}
Firefox 目前还不支持,需要渐进增强。
4. 伪元素不参与计数
::before / ::after 不算 sibling 节点,但可以在伪元素里用 sibling-index()——此时以 originating element 为基准计算。
下一步
去 caniuse 查一下当前覆盖率,如果你的产品 Firefox 用户占比不高,直接用就行。典型适用场景:列表动画、动态 Grid、色相/透明度平铺序列。插一个元素自动刷新分布这种事,以前要写一整块 JS,现在两行 CSS 搞定。
评论区
登录后可评论。