Menu

CSS 媒体查询:断点、min-width 与 max-width

媒体查询只在条件成立时应用 CSS,例如屏幕达到某个最小宽度、使用深色配色方案或者使用鼠标指针。本文讲解 @media 语法、移动优先的断点、范围语法、prefers-color-scheme、JavaScript 中的 matchMedia,以及容器查询。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

一个例子看懂媒体查询

媒体查询用一个条件把 CSS 包起来。@media (min-width: 600px) 中的规则只在视口宽度至少为 600px 时生效:

预览是一个框架(frame),其中的媒体查询测量的是框架本身,而不是你的屏幕。 在桌面电脑上,它大约和这篇文章的正文栏一样宽,通常超过 600px,所以盒子是蓝色的。把浏览器窗口缩窄,直到框架小于 600px,盒子就会变成琥珀色;盒子下面那一行会实时显示框架的宽度。在手机上框架本来就很窄,所以你看到的是琥珀色的版本。

@media 语法

@media (min-width: 600px) { ... }                        /* one condition */
@media (min-width: 600px) and (max-width: 899px) { ... } /* both must be true */
@media (max-width: 599px), print { ... }                 /* either one (comma = or) */
@media not print { ... }                                 /* negation */
@media screen and (orientation: landscape) { ... }       /* media type plus a feature */
@media (width >= 600px) { ... }                          /* range syntax */
@media (600px <= width < 900px) { ... }                  /* a range with both ends */

and 两边都需要空格:(min-width: 600px)and(max-width: 900px) 是无效的,整条查询都会被忽略。范围语法(width >= 600px)在当前的 Chrome、Firefox 和 Safari 中都已支持,读起来比 min-width 更清楚,尤其是表达“到某个值为止但不包括它”的时候。

最常用的媒体特性:

特性匹配条件示例
min-width / width >=视口至少有这么宽(min-width: 768px)
max-width / width <=视口最多有这么宽(max-width: 767px)
orientationportrait(高大于宽)或 landscape(orientation: landscape)
hover主指针能悬停(hover)或不能(none,大多数触摸屏)(hover: hover)
pointer主指针是 fine(鼠标)或 coarse(手指)(pointer: coarse)
prefers-color-scheme用户选择了 dark 或 light 主题(prefers-color-scheme: dark)
prefers-reduced-motion用户在系统中要求减少动态效果(prefers-reduced-motion: reduce)
print(一种媒体类型)页面被打印或另存为 PDF@media print

移动优先:min-width 断点

把手机布局写成基础 CSS,再添加 min-width 查询,随着空间变大改变布局。每条查询只需要描述发生变化的部分:

在较宽的桌面窗口中,预览超过 720px,所以你会看到三列;网格下面那一行会显示宽度和列数。把窗口缩窄,小于 720px 时变成两列,小于 480px 时变成一列。两条查询的顺序很重要:在宽屏上它们都成立,写在后面的那条胜出,所以 min-width 查询要从小到大排列。(max-width 查询则相反:从大到小。)

如何选择断点

并没有官方列表。下面是一些常见取值,以及它们通常划分的设备:

断点通常对应
min-width: 640px横屏的大屏手机、小平板
min-width: 768px竖屏的平板
min-width: 1024px横屏的平板、小尺寸笔记本
min-width: 1280px桌面电脑

更好的方法是:从窄布局开始,逐渐把窗口拉宽,直到某处看起来不对劲(行太长、卡片太宽),就在那里加一个断点。大多数页面有两三个断点就够了。很多时候你根本不需要断点:grid 中的 repeat(auto-fit, minmax(200px, 1fr))、flex-wrap 和 clamp() 都能连续地自适应。

每个页面都需要 viewport meta 标签

如果 <head> 中没有这个标签,手机浏览器会把页面当成屏幕大约有 980px 宽来排版,然后再缩小显示,于是你的 min-width: 480px 查询永远成立,手机布局永远不会出现:

<meta name="viewport" content="width=device-width, initial-scale=1">

meta viewport 页面解释了其中的每一个值。

用 prefers-color-scheme 实现深色模式

prefers-color-scheme 跟随操作系统的浅色或深色设置。最简洁的用法是把颜色放在 CSS 变量里,在查询中只修改这些变量:

在这个预览中,即使你的系统设置为深色,查询也可能输出 false:Chrome 不会把系统设置传进这种沙盒框架。按钮会手动设置同样的变量,网站上的主题切换按钮就是这样工作的。:root[data-theme] 比查询里普通的 :root 优先级更高,所以读者的选择会覆盖系统设置。把示例保存为文件再打开,就能看到查询跟随你的系统设置。

脚本展示了媒体查询的另一半:matchMedia() 在 JavaScript 中运行同样的查询,返回它当前是否匹配,并在结果变化时触发 change 事件。当行为(而不仅仅是样式)取决于屏幕时就用它,例如在触摸设备上用不同的方式打开菜单。

动画也是同样的道理。把大幅度或循环播放的动画放在 prefers-reduced-motion 检查中,具体见动画页面。

容器查询:让组件自己决定

媒体查询只知道视口。同一页面中,一张卡片既可能放在宽的主栏里,也可能放在窄的侧边栏里,它需要知道的是它自己的空间。这就是容器查询:用 container-type: inline-size 标记父元素,然后用 @container 写同样的条件。

拖动灰色盒子的角来调整它的大小。无论窗口多大,卡片都会在盒子宽度达到 360px 时切换布局:

在初始的 480px 下,图片位于文字旁边。把盒子拖到窄于 376px(360px 的内容加上两侧各 8px 的内边距),图片就会移到文字上方。所有当前的主流浏览器都支持容器查询。

打印样式

@media print 只在页面被打印或另存为 PDF 时生效。隐藏导航和按钮,并使用白底黑字:

@media print {
  nav, .ad, button { display: none; }
  body { color: black; background: white; }
  a::after { content: " (" attr(href) ")"; } /* print the URL after each link */
}

常见错误

  • 没有 viewport meta 标签。 这样手机会报告一个很宽的布局视口,针对小屏幕的查询永远不会匹配。
  • 查询的顺序写错。 两条成立的查询使用同一个选择器时,后写的那条胜出。min-width 查询要从小到大排列。
  • 边界重叠或出现空档。 max-width: 768px 和 min-width: 768px 在正好 768px 时都会匹配。其中一个改用 767px,或者使用范围语法 (width < 768px) 和 (width >= 768px)。
  • and 后面没有空格。 and( 是无效的,整条查询会被悄悄丢弃。
  • 只靠缩放桌面窗口来测试。 还要在真实手机或浏览器的设备模式下检查,设备模式会应用手机的视口、像素比和触摸。
  • 断点太多。 如果每个组件都需要自己的断点,可以改用 grid 的 auto-fit、flex-wrap 或容器查询。

常见问题

CSS 中的媒体查询是什么?

媒体查询是一条规则,只有当关于设备或窗口的某个条件成立时,才会应用其中的 CSS。@media (min-width: 600px) { ... } 只在视口宽度至少为 600px 时应用其中的样式,同一个页面正是靠它从手机适配到桌面电脑的。

媒体查询应该用 min-width 还是 max-width?

大多数网站采用移动优先的写法:基础 CSS 针对小屏幕,再用 min-width 查询随着屏幕变宽添加布局。max-width 查询则反过来,从桌面布局开始写。两种都可以;但在同一个元素上混用两者,会在边界处造成重叠和空档。

媒体查询的标准断点是多少?

并没有官方标准。常见的取值大约是 640px、768px、1024px 和 1280px,但更好的原则是:在你自己的布局开始显得不对劲的地方加断点,并且让断点的数量尽量少。

为什么我的媒体查询在手机上不生效?

最常见的原因是缺少 viewport meta 标签。没有 <meta name="viewport" content="width=device-width, initial-scale=1"> 时,手机浏览器会假装页面大约有 980px 宽,所以针对小宽度的查询永远不会匹配。另外还要检查查询是否写在它要覆盖的规则之后。

如何为深色模式写媒体查询?

使用 @media (prefers-color-scheme: dark) { ... }。当用户的操作系统或浏览器设置为深色主题时,它就会匹配,你可以在其中覆盖颜色或 CSS 变量。

媒体查询和容器查询有什么区别?

媒体查询检测的是视口(整个窗口)。容器查询用 @container 编写,检测的是某个父元素的尺寸,这个父元素需要用 container-type: inline-size 标记。如果一个组件在同一页面中既出现在宽的位置又出现在窄的位置,就用容器查询。

Coddy 编程语言插图

和 Coddy 一起学编程

开始