应使用 horizontal-viewport-segments 和 vertical-viewport-segments 媒体特性检测折叠状态,通过 @media 修改 :root 变量透传状态,结合 grid-template-areas 和 repeat(auto-fit, minmax()) 实现语义化三态布局,并避免 calc() 中变量未重求值问题。

用 horizontal-viewport-segments 和 vertical-viewport-segments 检测折叠状态
别再靠 min-width 或 prefers-holographic 猜设备形态——这两个媒体特性已在 Chrome 110+、Edge 110+、Safari 16.4+ 原生支持,且行为稳定。关键点在于:它们本身不能直接赋值给 CSS 变量,必须通过 @media 块修改 :root 来透传状态。
常见错误是同时匹配多个查询导致变量覆盖,建议统一用一个变量表达状态:
-
@media (horizontal-viewport-segments: 2) { :root { --fold-state: horizontal; } }→ 书本式展开(左右双屏) -
@media (vertical-viewport-segments: 2) { :root { --fold-state: vertical; } }→ 翻盖式折叠(上下双屏) -
:root { --fold-state: single; }→ 必须显式设默认值,否则未命中时var(--fold-state)返回空字符串,后续calc()或switch类逻辑会失效
用 grid-template-areas + @media 切换三态骨架
折叠屏不是“多一屏”,而是布局语义变了:单屏是线性流,双水平屏要并排主/侧,双垂直屏常需上下分层。硬写 display: none 或 width: 0 会破坏屏幕阅读器焦点流和 CSS 动画触发。
正确做法是用 grid-template-areas 定义语义区域,再按 --fold-state 切换排列:
- 单屏:
grid-template-areas: "header" "main" "sidebar" "footer"; - 水平双屏:
grid-template-areas: "header header" "main sidebar" "footer footer"; - 垂直双屏:
grid-template-areas: "header header" "main main" "sidebar sidebar" "footer footer";(注意:此时sidebar实际在右侧,但区域名保持语义不变)
所有参与 grid-area 的元素必须是网格容器的直接子元素;嵌套层级多了,grid-area 就不生效。
用 repeat(auto-fit, minmax()) 配合折叠状态动态控列
卡片类内容在折叠屏上不能简单“缩成一列”或“强行两列”——水平双屏下应自动铺满两个视口宽度(比如 2 × 480px),而垂直双屏下仍需单列保证可读性。这时候不能只靠 min-width 断点,得把折叠状态注入 Grid 计算。
方案是结合 --fold-state 变量与 minmax() 的最小值参数(虽然 CSS 不支持在 minmax() 内直接用 var(),但可通过降级策略绕过):
- 水平双屏时,设
--card-min: 480px;,然后grid-template-columns: repeat(auto-fit, minmax(var(--card-min), 1fr)); - 垂直双屏时,设
--card-min: 320px;,同上一行代码复用 - 单屏时,设
--card-min: 320px;并加@media (max-width: 480px)保底
注意:auto-fit 会收缩空轨道,auto-fill 会保留空列占位,后者在折叠屏小视口下易引发横向滚动条。
为什么不能对 --sidebar-span 做 calc() 运算
很多开发者想让侧边栏宽度随折叠状态动态减去边距,比如写 flex-basis: calc(var(--sidebar-span) - 80px);。这在 Chrome 和 Safari 中存在已知缺陷:当 --sidebar-span 因媒体查询切换而更新时,calc() 表达式不会重新求值,导致宽度卡死。
更可靠的做法是把偏移量也做成变量,并在同一个 @media 块里同步更新:
:root { --sidebar-span: 320px; --sidebar-offset: 0; }@media (horizontal-viewport-segments: 2) { :root { --sidebar-span: 600px; --sidebar-offset: 80px; } }- 然后用
flex-basis: calc(var(--sidebar-span) - var(--sidebar-offset));
这个细节容易被忽略——它不是语法问题,而是浏览器渲染管线中变量依赖链的触发时机问题。真正在意折叠体验的项目,必须在开发阶段就用真机或模拟器验证变量切换后的实时重绘效果。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











