你以为 select 高度只能靠 JS 算?Chrome 135 用 calc-size() 把这件事彻底原生化了
不是不想管,是管不了。浏览器的 UA 默认样式里,Picker 的 max-block-size 默认是 stretch——意思是它会撑到视口边缘占满。听起来挺合理,但当你把它拖到页面顶部,它就被压缩得只剩一条细线,什么选项都看不见。你想给它设个最小高度?min-height: 200px 对 intrinsic size 不起作用。你想设最大高度?max-height 在 stretch 面前也是个摆设。
Chrome 135 开始,这件事被 calc-size() 彻底原生化了。
什么是 calc-size()
calc-size() 是 CSS Values Level 5 里的新函数,专门用来对 intrinsic size 关键字做数学运算。它的语法是:
width: calc-size(fit-content, size + 2rem);
height: calc-size(auto, size * 0.7);
第一个参数是 intrinsic size 关键字(auto / fit-content / stretch / min-content / max-content),第二个参数是表达式,size 关键字代表第一个参数解析出来的实际尺寸。
这意味着你可以对「内容自适应」的高度做计算,而不只是对固定像素值做计算。
Goldilocks 三段式高度控制
Jake Archibald 在他的文章里总结了一套 select picker 的「不多不少刚刚好」高度方案,三个维度各有各的痛点,对应三个 calc-size() 解法。
第一,Picker 不能碰到视口边缘。
默认 UA 样式 max-block-size: stretch 会让 Picker 撑满整个可用空间,但当你把它放到页面顶部,它就溢出了。解决方案是用 position-try-fallbacks: flip-block/flip-inline 让它自动向上翻转,同时用 margin-block-end 留出空白。Firefox 不支持 stretch,就用 max-block-size: calc(100% – var(–viewport-margin)) 做兜底。
第二,Picker 被压缩得太小。
当 Picker 被拖到视口边缘时,Chrome 默认的 min-block-size: 1lh 会让它变得只有一行文字高,完全不可用。真正想要的是:跟内容走,但如果内容太少也不能小于某个值。
这就是 calc-size() 的用武之地:
min-block-size: calc-size(fit-content, min(size, 12em));
fit-content 让高度跟随内容,min(size, 12em) 确保最小不低于内容本身,最大不超过 12em。size 关键字在这里是画龙点睛——它代表 intrinsic size 解析后的实际值,你可以在这个值上做任何 min/max/clamp 计算。
第三,Picker 撑得太大。
当选项很多时,Picker 会占满整个视口,看起来像一个全屏弹窗。解决方案:
max-block-size: calc-size(stretch, min(size, 30em));
stretch 取锚点定位的可用空间,min(size, 30em) 设了 30em 的上限。
配合 ::picker(select) 使用
这三个技巧都作用在 ::picker(select) 伪元素上,这是 CSS Form Control Styling Level 1 规范的一部分。启用自定义 select 的方式很简单:
select, ::picker(select) {
appearance: base-select;
}
三行 CSS,你就有了一个可以用纯 CSS 控制高度、定位、样式的原生 select。Chrome 135+ 默认支持,Safari 27 是 preview 状态,Firefox 暂不支持。
动画也支持了
calc-size() 的返回值天然支持插值动画。结合 interpolate-size: allow-keywords,你可以写出这样的展开动画:
:root { interpolate-size: allow-keywords; }
.panel {
height: 0;
transition: height 300ms;
}
.panel.open {
height: calc-size(auto, size);
}
这不再是 max-height: 9999px 的 hack,而是真正的 intrinsic size 动画。Chrome/Edge 129+ 支持,Firefox/Safari 回退到无动画的 snap 行为(安全降级)。
渐进增强写法
.custom-select::picker(select) {
min-block-size: calc-size(fit-content, min(size, 12em));
@supports not (min-block-size: calc-size(fit-content, min(size, 1px))) {
min-block-size: 12em;
}
}
不支持 calc-size() 的浏览器会走 @supports not 分支,回退到一个固定最小高度,不影响基本功能。
下一步
- 打开 Chrome 135+,直接试 appearance: base-select + calc-size() 组合,用 DevTools 看 ::picker(select) 的实际表现
- 访问 Jake Archibald 的 Demo(https://www.jakearchibald.com/2026/goldilocks-select-height),拖拽感受 Goldilocks 三段式高度
- 找项目里用 max-height: 9999px 或 ResizeObserver 做展开动画的地方,换成 interpolate-size: allow-keywords + calc-size(),删掉 JS 计算
评论区
登录后可评论。