学完这篇,你能掌握一套可以直接抄进项目的「移动端优先」媒体查询写法——手机样式当默认,桌面靠 min-width 层层叠加,从此告别「改了桌面样式结果手机也乱了」。
第一步:先把三行基础设施写对
没有这三行,后面所有媒体查询都是在沙子上盖楼。
<meta name="viewport" content="width=device-width, initial-scale=1">
*, *::before, *::after { box-sizing: border-box; }
img, video { max-width: 100%; height: auto; display: block; }
box-sizing: border-box 让 padding 不再撑破宽度,图片 max-width: 100% 让它在窄屏里自动缩放。这两个几乎是响应式的地基。
注意:不要写 maximum-scale=1, user-scalable=no 来「防止用户缩放」。这会破坏可访问性,iOS 上还会让输入框聚焦时被迫放大。
第二步:默认样式就是手机样式
移动端优先的核心不是「加媒体查询」,而是基础样式天然就是窄屏的样子,媒体查询只负责「屏幕变宽了、改点什么」。
/* 基础:单列、竖排,手机默认 */
.card-list { display: block; }
.card { padding: 16px; font-size: 15px; }
.nav { display: flex; flex-direction: column; }
写的时候心里默念:我现在是在给一台 375px 宽的手机写页面。写完能用了,再往上加宽屏。
第三步:只用 min-width 递增,不要混 max-width
@media (min-width: 640px) {
.card-list { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.nav { flex-direction: row; }
}
@media (min-width: 1024px) {
.card-list { grid-template-columns: repeat(3, 1fr); }
}
这样写的好处是:小屏不需要知道大屏存在,样式只在你需要改变的地方被覆盖,不会出现「手机被桌面的 max-width 规则误伤」。
注意:max-width: 767px 和 min-width: 768px 看起来互不重叠,但 767.98px 这类设备会掉进缝隙。混用两套逻辑是响应式最常见的返工来源,项目里统一选一套。
第四步:断点按内容定,不要按机型定
别去背 iPhone 14 是多少像素。把浏览器窗口从窄到宽慢慢拉,在哪一刻布局开始难看,那里就是断点。
一套常见的起步值:
/* 640px 小屏手机横屏 / 大手机 */
/* 768px 平板竖屏 */
/* 1024px 平板横屏 / 小笔记本 */
/* 1280px 桌面 */
断点数量控制在 3~4 个,每个都写清楚注释它「为什么存在」。
第五步:能用流式布局就别用断点
很多断点其实是多余的。下面这行 Grid 代码能自动根据容器宽度决定排几列,从手机到超宽屏全包,一个媒体查询都不用:
.card-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 16px;
}
再配合 clamp() 做流体字号和间距,能砍掉大量断点:
h1 { font-size: clamp(1.5rem, 4vw + 1rem, 2.5rem); }
.section { padding: clamp(16px, 4vw, 48px); }
原则:能量变,就别用媒体查询跳变。
第六步:把「能不能悬停」也当成一种条件
触摸屏上 :hover 会变成「点一下粘住」的恼人效果。用能力检测隔离它:
@media (hover: hover) and (pointer: fine) {
.btn:hover { background: #0056b3; }
}
同理,尊重用户的系统偏好:
@media (prefers-reduced-motion: reduce) {
* { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
第七步:三个移动端高频坑
坑一:100vh 在手机上会被地址栏截断。 改用动态视口单位:
.hero { min-height: 100dvh; } /* 需要兼容时:100vh + @supports (height: 100dvh) 覆盖 */
坑二:点击区域太小。 移动端可点元素建议不小于 44×44px,图标按钮记得加 padding 而不是只放大字号。
坑三:忘了在高 DPI 屏上检查 1px 边框。 不需要专门写 -webkit-min-device-pixel-ratio 媒体查询,用 transform: scaleY(.5) 或直接接受 2px 观感即可,过度优化反而增加维护成本。
第八步:能上容器查询就上
组件复用时,它真正在意的是自己所在容器多宽,而不是整个屏幕多宽。用容器查询可以让同一个卡片在侧边栏窄、在主区域宽时呈现不同形态:
.card-wrap { container-type: inline-size; }
@container (min-width: 480px) {
.card { display: flex; gap: 16px; }
}
注意:容器查询需要给父级声明 container-type,且被查询的容器不能依赖子元素尺寸(否则会形成循环)。老项目可以先只在独立组件里用。
小结
- 默认样式即手机样式,媒体查询只做「变宽后覆盖」,全程用
min-width 递增。
- 断点按内容需要定,3~4 个足够,不要背机型像素。
- Grid 的
auto-fit + minmax 和 clamp() 能消掉大部分断点,优先用它们。
@media (hover: hover) 隔离悬停效果,prefers-reduced-motion 尊重系统设置。
- 高度用
dvh 而非 vh,点击区域不小于 44px。
- 组件级适配优先考虑
@container,屏幕级适配才用 @media。