13响应式与媒体查询

一套 HTML + 一份 CSS,如何同时在手机、平板、桌面三端都好看? 答案就是响应式(Responsive)三支柱:媒体查询 @media(按视口条件切换样式)、 流式单位(% / vw / clamp() 让尺寸随屏幕呼吸)、 移动优先(先做小屏,再 min-width 渐进增强)。 今天把三大支柱一次学完——再到第 14 课学 Flex / Grid 布局。

约 18 分钟 6 个动手实验 手机 / 平板 / 桌面

一、媒体查询 @media:CSS 的 if 判断

媒体查询的语法极简——@media (条件) { 样式 }, 条件成立才生效,不成立就当不存在。和 JS 的 if 一个意思。 最常用条件是「视口宽度」,进阶还有横竖屏、系统主题、指针精度等。

媒体特性速查:条件名、合法值、适用场景
媒体特性常用值判断什么典型场景
min-width / max-width768px 等长度视口宽度阈值断点切换布局,99% 场景都靠它
orientationportrait / landscape横屏还是竖屏视频全屏、游戏场景
prefers-color-schemelight / dark系统主题暗色模式适配
hoverhover / none是否有悬停能力手机 hover:none,按钮可隐藏悬浮菜单
pointerfine / coarse / none指针精度coarse=手指(增大点击区)、fine=鼠标
prefers-reduced-motionreduce / no-preference用户是否要减少动效关掉过场动画,无障碍标配
同一卡片在不同视口下换色:把 800px 改成 500px 看阈值变化;拖动浏览器边缘(或 DevTools Toggle Device)实际看效果
现代推荐写法:永远用 min-width(移动优先),而不是 max-width(桌面优先)。 原因——同一份样式,写 min-width 时只需要从最小屏往大覆盖,写 max-width 时要不停覆盖桌面样式, 后者会陷入「补丁叠补丁」的混乱。

二、断点选择:按内容而非设备

「断点」就是媒体查询的临界宽度。没有标准答案—— 业界常见的 768 / 1024 只是历史经验,真正决定断点的是你自己的内容什么时候开始变形。 三个主流框架的参考体系如下:

三家主流断点体系对照:Bootstrap / Tailwind / Material
设备类型Bootstrap 5Tailwind 3Material 3常见内容临界
手机< 576px< 640px0 ~ 599dp单列布局、所有内容垂直堆叠
大手机 / 小平板≥ 576px≥ 640px600 ~ 839dp可放两列、字号放大
平板≥ 768px≥ 768px840 ~ 1199dp多列布局、侧边栏登场
桌面≥ 992px≥ 1024px1200 ~ 1599dp完整布局、最大宽度封顶
大屏≥ 1200px≥ 1280px≥ 1600dp居中留白、内容不超过 1200px
实战建议:先做手机版,往宽里看,到内容「撑不住」时再加断点—— 比如一行文字超过 70 个字符读起来累,就在那个宽度加断点。 别死记硬背 768 / 1024,按自己的内容决定。
iOS Safari 有个奇葩:横竖屏切换会触发地址栏显隐,导致视口高度瞬间变化。 如果用 100vh 做全屏背景,会出现「地址栏收起时多出一段空白」。 现代方案用 100dvh(dynamic viewport height)替代——它会跟随地址栏动态调整。

三、流式单位:% / vw / vh / vmin / vmax / clamp()

写死 px 的布局一换屏就崩。流式单位让尺寸跟随视口呼吸—— 视口有多大,元素就多大。六种视口单位各司其职:

六种视口单位对照:1vw/vh 等于视口宽/高的 1%
单位含义典型用法注意事项
%相对父元素子元素宽度 50%高度 % 要父元素有显式高度才生效
vw视口宽度的 1%全屏 banner、font-size: 4vw小屏会缩得太小、大屏会放得太大
vh视口高度的 1%全屏背景、移动端首屏iOS Safari 用 dvh 替代
vmin视口宽高较小者的 1%横竖屏都正方形的元素游戏 UI、保持比例的按钮
vmax视口宽高较大者的 1%横屏特别宽的设备桌面端很少用
clamp(MIN, FLUID, MAX)夹在三值之间流体字号 clamp(1rem, 2vw, 2rem)现代字号首选,一行顶十行
固定 vs 流式对照:上面「固定 24px」永远 24px;下面 clamp 流体字号会跟着浏览器宽度变大变小(拖动浏览器边缘看效果)
clamp 公式的记忆诀窍:「基准 + 斜率 × vw」。 0.5rem + 2vw 的意思是——浏览器宽度为 0 时字号 0.5rem, 每增加 1vw 字号增加 0.02rem(因为 2vw = 0.02rem × 浏览器宽度 / 1), 最后用 clamp 夹出上下限。改「斜率」就能改响应速度。

四、移动优先 vs 桌面优先

「移动优先」(Mobile First)= 先写最小屏的样式,再用 min-width 渐进增强; 「桌面优先」则反过来,先写桌面,再用 max-width 优雅降级。 现代工程统一推荐移动优先——代码更少、维护更容易、性能更好。

同一卡片两种写法对比:上面移动优先(3 行)vs 下面桌面优先(4 行 + 更多覆盖)—— 后者越改越乱
三句话总结:① 默认样式就是手机(最常见、最基础); ② min-width 往上加,max-width 几乎不用; ③ 改一次样式只改一个地方,不会牵一发动全身。

五、图片自适应:max-width: 100% + srcset

一张 800px 的大图塞进 300px 的手机容器,要么溢出、要么被裁——都是灾难。 两行 CSS 解决:max-width: 100%; height: auto, 图片永不超出容器,高度按比例自动算。 配合 srcset 还能给 Retina 屏喂高清图,普通屏喂小图省流量。

左侧盒子没限制 max-width:图片原始 800px 撑爆 300px 容器;右侧加 max-width:100% —— 图片自动缩到容器宽度(拖左侧盒子右沿看宽度变化)
进阶组合拳:object-fit: cover + object-position 让图片「裁剪对齐」。 详见「CSS 基础」第 4 课《背景与边框》里 object-fit 详解——同一个属性既能用给背景,也能用给 <img>。

六、容器查询 @container 横向对比(呼应第 12 课)

媒体查询是「视口多宽我就怎么排」; 容器查询(@container)是「我爹多宽我就怎么排」。 现代组件化开发,容器查询才是更优解——同一个卡片组件, 放在侧边栏和主内容区能各自独立适配,不再依赖视口。

@media vs @container:判断依据、写法、适用场景
维度@media 媒体查询@container 容器查询
判断依据视口(浏览器)宽度最近祖先容器的宽度
语法@media (min-width: 768px)@container (min-width: 400px)
前置声明不需要父容器需写 container-type: inline-size
适用场景整页布局(导航、栅格)可复用组件(卡片、徽章)
兼容性全浏览器支持Chrome 105+ / Safari 16+ / Firefox 110+
同一卡片组件,两种宽度的父容器各自适配:拖动右侧父容器右沿看卡片从竖排变横排(仅看父容器宽度,与视口无关)
选择诀窍:判断的是「视口维度」就用 @media(比如整页导航、底部页脚); 判断的是「父容器维度」就用 @container(比如同一张卡片要复用进侧边栏 / 主区 / 弹窗)。 一个项目通常两者混用,不冲突。

七、暗色模式适配:prefers-color-scheme

系统设置成「深色」时,网页还在用亮白背景——体验割裂。 prefers-color-scheme 让 CSS 自动跟随: @media (prefers-color-scheme: dark) { ... } 一段就搞定。 配合CSS 变量实现「一份定义、两套变量」——切换主题只换变量值, 不必复制粘贴一整份样式。

亮 vs 暗色卡静态对照:左侧是 prefers-color-scheme: light 下的样子,右侧是 dark 下的样子 —— 文字说明实际响应系统主题(Playwright emulateMedia 可验证)
两个常踩的坑:① 不要完全靠 prefers-color-scheme—— 用户可能在系统亮色下也想用网站的暗色版;高级站点会提供「手动切换开关」,用 JS 加 class 覆盖系统偏好。 ② 暗色模式别用纯黑 #000——纯黑 + 纯白对比度太刺眼, 推荐 #1A1A1A ~ #2A2218 这种「暗灰」做背景,#E8DDC9 这种「米白」做文字。
配套设置:除了 @media (prefers-color-scheme),还要在 <head> 里加 <meta name="color-scheme" content="light dark">—— 告诉浏览器「这个页面支持亮 + 暗主题」,原生滚动条、表单控件才会自动反色,否则手机浏览器会把表单染成惨白一片。

八、本章小结

  • 媒体查询 @media (条件) { ... } 是响应式的灵魂——按视口、横竖屏、系统主题等条件切换样式。
  • 断点按内容决定而非按设备;现代推荐 min-width(移动优先)。
  • 流式单位家族:% / vw / vh / vmin / vmax,最实用的是 clamp(MIN, FLUID, MAX) 流体公式。
  • 图片自适应两行 CSS 标配:img { max-width: 100%; height: auto; },配合 srcset 给 Retina 屏喂高清图。
  • @container 容器查询让组件知道自己父容器多宽——比媒体查询更适合可复用组件。
  • 暗色模式三件套:@media (prefers-color-scheme: dark) + CSS 变量 + <meta name="color-scheme">。
  • 全站响应式开发流:移动优先 + 流式单位 + 容器查询组件 + 图片自适应 + 暗色模式。
动手练习:给自己手头的项目做四件事—— ① 把主样式改成移动优先:默认就是手机,所有断点改用 min-width; ② 用 clamp() 给主标题做流体字号(试试 clamp(1.5rem, 1rem + 2vw, 3rem)); ③ 给所有 <img> 加 max-width: 100%; height: auto; 让图片永不溢出; ④ 把颜色都改成 CSS 变量,加一段 @media (prefers-color-scheme: dark) 支持暗色模式。 改完拿 DevTools 切到手机 / 平板 / 桌面三种宽度验证。