horizontal-viewport-segments: 2 是唯一可靠信号,因其直接读取系统上报的物理双视口分割状态,匹配时 document.body.clientWidth 返回单侧宽度(375–420px),非总宽;必须严格写为 2、不可嵌套、需 @supports 降级并搭配防抖监听 visualViewport.width。

直接用 @media (horizontal-viewport-segments: 2) 判断竖折设备(如 Galaxy Z Fold、Pixel Fold)真正展开态,这是目前唯一能稳定区分“双屏物理分割”和“普通大屏”的方案;@media (min-width: 768px) 或 @media (orientation: landscape) 在折叠动画中会反复触发、误判 iPad 和横屏手机,实际等于没适配。
为什么 horizontal-viewport-segments: 2 是唯一可靠信号
这个媒体特性反映的是浏览器是否正在以两个独立水平视口渲染页面,不是猜宽度,而是读系统上报的物理分割状态:
-
document.body.clientWidth在匹配时返回单侧宽度(约 375–420px),不是总宽——别用它算栅格列数 - 值必须严格写成
2,不能是>1、=2或带单位,否则 Chrome/Edge 119+ 也会静默忽略 - 不能嵌套在其他查询里,比如
@media (min-width: 720px) and (horizontal-viewport-segments: 2)会失效 - 匹配后适合用
grid-template-areas布局:左侧固定"sidebar"+ 右侧自适应"main",比 flex 更稳
如何安全降级兼容 Safari/Firefox
Safari 16.4+ 和 Firefox 完全不支持 horizontal-viewport-segments,不包裹 @supports 就会导致整条规则被跳过:
- 必须用
@supports (horizontal-viewport-segments: 2)包裹所有相关样式,不能只包一部分 - 降级逻辑要覆盖“设备已展开但浏览器不识别”的场景,比如默认单栏,只在匹配时激活双栏
- 可搭配 JS fallback:
matchMedia('(min-width: 720px) and (min-height: 840px)')+navigator.userAgent.includes('Fold')粗筛 - 避免用
screen-spanning:它已被 Chromium 标记为废弃,MDN 移除文档,所有浏览器静默忽略
JS 监听必须防抖且读 visualViewport.width
CSS 媒体查询只响应静态状态,折叠过程中的半开、动画帧、系统延迟上报会导致频繁误触发:
立即学习“前端免费学习笔记(深入)”;
- 监听必须用
window.matchMedia('(horizontal-viewport-segments: 2)'),不是resize事件 - 回调里读
visualViewport.width而非window.innerWidth,后者在动画中跳变剧烈 - 加 200–300ms 防抖:Z Fold 展开瞬间可能触发 4–6 次 change,不处理 UI 会疯狂重排
- 更新 DOM 优先用
requestAnimationFrame批量切换类名,别直接操作样式属性
真正难的不是写出那行 @media,而是降级时有没有覆盖到 Safari 16.3 下用户已展开设备但规则完全不生效的场景——此时若没 fallback,双栏就彻底消失,且毫无报错提示。


















