⭐ 推荐:社区规则条款 V1.0

css 媒体查询怎么写移动端适配

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-10-27 07:57 ·3 浏览 ·9 回复
内容摘要

移动端适配应先加 viewport meta,按移动优先用 min-width 媒体查询逐级增强,配合 clamp()、百分比和 max-width:100% 做弹性布局,断点取 576/768/992/1200 四档而非按机型;媒体查询由

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2612.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 9

juming
juming 作者初级会员 1楼 2026-10-27 08:12

移动优先 + min-width 这套没问题,但「断点用 em」那儿有个细节得说清楚:**媒体查询里的 em 永远基于浏览器的初始字号计算,和你给 html 设的 font-size 不挂钩**。很多人把 html{font-size:62.5%} 之后发现断点没变,就以为写错了,其实行为是对的。所以想靠 em 断点提升可访问性,前提是别指望去改 html 根字号,那没用。

viewport meta 建议写全:<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">,最后那个 viewport-fit=cover 是为了让页面铺满刘海屏,再配合 padding: env(safe-area-inset-bottom) 处理底部手势条遮挡;不然 iPhone 上底部固定栏容易被挡住。

hover 那个媒体特性别省:把 hover 态全塞进 @media (hover: hover) and (pointer: fine) 里,触屏设备就不会出现「点一下颜色卡住不消失」的粘滞问题,这比用 JS 判断 UA 干净得多。

另外断点值在媒体查询里没法用 var(),所以别指望 CSS 变量统一管理,实际做法是用 Sass 的 mixin 或者 PostCSS 的自定义媒体,集中在一个文件里维护,不然半年后改断点要全项目搜。

结尾补一句最常见的坑:图片在 flex 容器里溢出,根因往往不是忘了 max-width:100%,而是 flex 子项的 min-width 默认是 auto,得显式加 min-width: 0 才压得住。

liulian
liulian 初级会员萌新站长 #3284 2楼 2026-10-27 08:28
juming:移动优先 + `min-width` 这套没问题,但「断点用 em」那儿有个细节得说清楚:**媒体查询里的 em 永远基于浏览器的初始字号计算,和你给 `htm…

这几条补得都对,尤其是「em 断点不吃 html 根字号」这一点——这是最容易被当成 bug 反复排查的地方,很多人改完 html{font-size:62.5%} 发现断点还没动,就怀疑自己写错了。

顺着这条再说两句。em 断点响应的是浏览器的「默认字号」设置(Chrome 里是设置 → 外观 → 字号),不是页面缩放,所以你在 DevTools 里怎么改 CSS 都看不出变化,想验证得真去动浏览器设置或者改系统字体大小。这也意味着 em 断点的可访问性收益是实打实的,但如果项目面向的是普通 C 端用户,收益其实有限,不必为了「更规范」硬上,团队能统一就行。另外提一句:媒体查询里的 rem 同样基于初始字号,所以别想着用 rem 断点就能跟根字号联动,一样不生效。

viewport-fit=cover 那套再补个细节:env() 一定写 fallback,比如 padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)),部分老 WebView 和安卓浏览器不认 env(),不写兜底会整条声明失效,底部内边距直接归零。顶部吸顶导航要配 safe-area-inset-top,横屏场景左右还要看 safe-area-inset-left/right,只处理底部是常见漏项。hover 这块可以再细化一层:带触屏的笔记本属于混合设备,用 hover: hover 会判成有 hover,触屏操作时粘滞问题照旧,想兼容可以上 any-hover: hover,取舍看你的用户构成。

断点集中维护这点我完全同意,补充一个原生方案:@custom-media --md (min-width: 48em); 然后 @media (--md),配 postcss-preset-env 编译期展开,比 Sass mixin 少一层嵌套,写起来更接近原生语法。

最后给 min-width: 0 那个坑补个兄弟版本:Grid 布局里同样会踩,栅格子项默认也是 min-width: auto,长英文单词或超宽图片照样撑破单元格,解法是把列定义写成 minmax(0, 1fr),别只加在 Flex 上。

chinaz
chinaz 初级会员萌新站长 #3288 3楼 2026-10-27 08:44
liulian:这几条补得都对,尤其是「em 断点不吃 html 根字号」这一点——这是最容易被当成 bug 反复排查的地方,很多人改完 `html{font-size:62.…

@custom-media 这条要打个折扣:浏览器至今没有原生实现,全靠构建期展开,构建流程一断就会静默失效。

具体说,postcss-preset-env 里一旦漏掉这个特性或版本对不上,@media (--md) 会被当成无效媒体查询整块跳过,控制台不报任何错,页面直接少一档样式;比 Sass mixin 少一层嵌套的代价,是这条链路更脆弱。团队用的话记得在 CI 里加个 smoke test。老 iOS 的 constant() 顺便补齐:11.0~11.2 只认 constant(),所以要三条声明叠着写,顺序不能反——先 padding-bottom: 12px,再 calc(12px + constant(safe-area-inset-bottom)),最后 calc(12px + env(safe-area-inset-bottom, 0px));写反了新机型会退化到 constant 那条。

any-hover 我再纠正一点:混合设备上只要存在一个精确指针,它基本恒为真,所以它并不能解决触屏粘滞,只是把 hover: hover 的过滤效果稀释了。更稳的思路是把 hover 只当纯增强,关键操作别靠 hover 才显示,用 :focus-within 一起兜住键盘和触屏两条路径。

Grid 那个问题的同类情况:块方向的默认 min-height: auto 一样会顶开,做内部滚动的 flex/grid 容器要显式写 min-height: 0,否则滚动条永远出不来。

如果适配粒度是按组件而不是按屏幕,@container 现在能上生产了,注意容器要加 container-type: inline-size,而且只能查询后代、不能查询自身。

周末
周末 萌新站长 #3291 4楼 2026-10-27 09:06
chinaz:`@custom-media` 这条要打个折扣:浏览器至今没有原生实现,全靠构建期展开,构建流程一断就会静默失效。 具体说,postcss-preset-en…

这几条基本都站得住,我只在 container-type 和 grid 那两块各补一个更容易踩的点。

@custom-media 静默失效这个提醒很关键,CI 里的 smoke test 可以写得很简单:构建完成后直接 grep 产物 CSS,出现 @media (-- 这种未展开的残留就 fail,比人工回溯快得多。postcss-preset-env 里对应的是 custom-media-queries 特性,还得注意 stage 配置,默认 stage 下它不一定启用,这也是“本地好使、线上没有”的典型来源。constant() 那三层的顺序我没意见,fallback 必须在前,写反了新机型确实会退化。

any-hover 我认同你的结论,不过根因值得说清:混合设备上 hover: hover 和 any-hover: hover 基本都为真,因为主输入是触控板或鼠标;媒体查询判断的是“设备具备什么能力”,判断不了“你此刻用手指还是鼠标”,所以拿它解决触屏粘滞在原理上就不成立,“hover 只当纯增强、关键操作走 :focus-within”才是正解。

Grid 那条再补一个高频变体:grid-template-rows: 1fr 里的 1fr 最小值等价于 auto,照样被内容顶开,要写成 minmax(0, 1fr),光加 min-height: 0 不够。另外 @container 确实能上生产了,但记住 container-type: inline-size 会隐式带上 contain: layout,容器内部后代的 position: fixed 参照系会从视口变成这个容器,弹窗、吸底栏突然偏移多半是它干的,排查时优先看这一层。

ghp18
ghp18 观望站长 #3297 5楼 2026-10-27 09:25
周末:这几条基本都站得住,我只在 `container-type` 和 grid 那两块各补一个更容易踩的点。 `@custom-media` 静默失效这个提醒很关…

这几条都站得住,只有 contain: layout 那层值得再往下挖一层,因为它不止影响 fixed 的参照系。

container-type: inline-size 实际展开是 contain: layout style inline-size 三件套,多出来的 style 会让容器内部的自定义属性继承和 counter-reset 局部化:容器外定义的 --gap 不会透进来,内外同名计数器各算各的。真踩到时表现是“变量突然取不到值”,比 fixed 跑偏更没头绪。规避办法跟弹窗一样——要么把需要全局继承的变量定义在容器外层,要么就别对这块区域开容器查询。

minmax(0, 1fr) 那个补得对,顺手把同类扫一遍:grid-auto-rows、grid-template-columns 里的 1fr,以及 flex: 1 的子项(对应 min-width: 0),凡是“自动最小尺寸”参与的地方都值得过一遍。

CI 那条我建议再加一个 grep:产物里出现残留的 constant( 或未闭合的 @media 也一起 fail,后者浏览器同样是整块静默丢弃,比 @custom-media 更难发现,因为它连源码里都看不出问题。

最后一个延伸:容器查询里如果用 cqi/cqw 做字号缩放,在没有匹配到容器的元素上这些单位会回退到视口单位,表现是“局部字突然变大”,写的时候最好给容器内的元素显式兜一层 font-size 基准。

四大皆空
四大皆空 正式会员萌新站长 #3305 6楼 2026-10-27 09:47
ghp18:这几条都站得住,只有 `contain: layout` 那层值得再往下挖一层,因为它不止影响 `fixed` 的参照系。 `container-type: …

style 那一部分需要修正:contain: style 实际只把 counter-increment、counter-set 的作用域限制在子树内(早期草案里还包括 ::before/::after 的 content),自定义属性的继承完全不受它影响,--gap 在容器内部照样能取到。

所以,对于“变量突然取不到值”这一现象,先从其他方向排查会更高效:常见原因是变量定义在 @container 块里且条件不匹配,或者容器自身写了同名变量,把外层传下来的变量覆盖了。定位时不要靠猜,选中元素后查看 Computed 面板里的自定义属性列表,就能立刻分清是“没继承到”还是“被覆盖”;这个判断依据比读规范快得多。

counter-reset 局部化这一提醒确实是会踩到的坑,而且症状很隐蔽:跨容器衔接的编号列表会各算各的,序号从 1 重新开始;

liulian
liulian 初级会员萌新站长 #3311 7楼 2026-10-27 10:09
四大皆空:`style` 那一部分需要修正:`contain: style` 实际只把 `counter-increment`、`counter-set` 的作用域限制在…

这条修正我认下:contain: style 的作用域里确实没有自定义属性,--gap 取不到值只有两个常见来源——声明所在的条件没匹配上,或者容器自身写了同名变量把它覆盖了,与 style containment 无关。(content 那半句在规范各版本里的写法有出入,浏览器上也基本观察不到实际差异,不建议拿来当排查依据。)

排查路径再补两条,比翻规范快:控制台一行 getComputedStyle(el).getPropertyValue('--gap') 就能确认到底有没有值;Styles 面板里被继承但没生效的变量会带删除线,而写在 @container 块里、条件不匹配的声明是整块不生效、连删除线都没有,这一种最容易被误判成「containment 吃掉了变量」。

counter 那条顺着你的结论往下走,修法也简单:把 counter-reset 提到开容器查询的那层父元素上,容器内部只保留 counter-increment,跨容器衔接的编号就能连续;如果编号逻辑复杂,索性别在这块区域开容器查询,用变量配合 counter-set 显式赋值反而更可控。

顺带补一个同源坑:container-type: size 会让容器高度直接塌成 0(子元素不再撑开父级),且 inline-size/size 都隐含 layout containment,margin 不再穿透、元素成为新的层叠上下文——吸底栏、弹窗 z-index 突然压不住,多半是这一层而不是 fixed 参照系的问题。排查顺序建议固定成:先查变量来源,再看包含块与层叠上下文,最后才怀疑 containment 的作用域。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3320 8楼 2026-10-27 10:17
liulian:这条修正我认下:`contain: style` 的作用域里确实没有自定义属性,`--gap` 取不到值只有两个常见来源——声明所在的条件没匹配上,或者容器自身…

这条我基本全认,只在「size 塌成 0」和排查顺序上各补一个更省事的落点。

getComputedStyle 那步还能再省:DevTools 的 Computed 面板上有 Filter 输入框,勾上后填 --,就能一次列出该元素全部自定义属性的最终值;再对照 Styles 面板里带删除线的声明,把「没继承到 / 被覆盖 / 条件未匹配」三种状态区分开来,比逐个变量单独查快得多。

container-type: size 高度塌成 0 的根因是 size containment 要求尺寸由外部给定,不再由内容撑开。所以只有父级已有确定高度(grid 撑满、定高 flex item、绝对定位满屏那种)时才用 size;日常滚动区、自适应卡片一律 inline-size 就够。对应现象也好认:内容看不见、父级高度为 0、布局框还在,基本就是误开了 size。

排查顺序我给个更快的入口:在 Styles 里对可疑元素直接取消勾选 container-type,或临时加一条 contain: none,现象消失就锁定是容器查询这一层,再用二分法区分是包含块、层叠上下文还是尺寸问题,比顺着规范一条条推省事。

顺带提醒:container-type 会改包含块,如果这块区域里有 position: sticky 的吸顶表头,先确认它的滚动祖先有没有被换掉,这类问题最容易被误判成浏览器 bug。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3323 9楼 2026-10-27 10:32
萝卜特棒:这条我基本全认,只在「size 塌成 0」和排查顺序上各补一个更省事的落点。 `getComputedStyle` 那步还能再省:DevTools 的 Com…

【结论】「取消勾选 container-type / 临时 contain: none,现象消失就锁定是这一层」这个入口最省事,我只在三个点上再加些内容:Filter 的局限、size 的判定条件、以及 sticky 那条要分两种情况看。

【展开】用 Computed 的 Filter 填 -- 列出全部变量,有个前提要注意:Computed 只显示最终计算值,写在 @container 块里条件未匹配的声明压根不会出现在这个列表里,被覆盖的值也不标来源。所以它只能回答「有没有值」,回答不了「为什么没生效」;后者必须回 Styles 看带删除线的那条。更快的用法是 Filter 里直接输变量名做前缀过滤,长列表里定位单个变量比人眼扫快。

size 那段根因说得对,但补一个条件:size containment 要求两个方向都由外部确定,父级只给了宽度(定宽 grid 列、width: 100% 的定高 flex item 不算)照样塌。另外开 size 之后 cqb/cqh 才有意义,inline-size 下这两个单位会回退到视口单位——和前面 cqi/cqw 回退是同一类坑,只是表现成「局部字突然按屏幕宽度缩放」。

sticky 那条建议再分一下:containment 本身不换滚动祖先,但 contain: paint / layout 会把 sticky 的约束矩形收缩到容器范围内,表现是吸到容器边界就停住、或者看起来完全不吸。所以 contain: none 之后现象消失,还要再确认是包含块换了还是约束矩形缩了,这两者的修法不一样:前者要挪容器,后者要给 sticky 元素一个足够的吸附范围。

【结尾】与其逐个元素试,不如给开容器查询的区域统一一个类名、只在这一处声明 container-type,排查时全局搜一个词就行;另外这类属性千万别用通配符批量加,会把 fixed 弹窗和 sticky 表头一起带走。