Menu

Медиазапросы в CSS: брейкпоинты, min-width и max-width

Медиазапрос применяет CSS, только когда условие истинно: минимальная ширина экрана, темная цветовая схема или указатель-мышь. Разберем синтаксис @media, брейкпоинты mobile first, синтаксис диапазонов, prefers-color-scheme, matchMedia в JavaScript и контейнерные запросы.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Медиазапрос на одном примере

Медиазапрос оборачивает CSS в условие. Правила внутри @media (min-width: 600px) действуют, только пока окно просмотра шириной не меньше 600px:

Превью это фрейм, и медиазапрос внутри него измеряет фрейм, а не ваш экран. На компьютере он примерно шириной с колонку этой статьи, обычно больше 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

Mobile first: брейкпоинты min-width

Напишите раскладку для телефона как базовый CSS, а затем добавьте запросы min-width, которые меняют раскладку по мере роста пространства. Каждый запрос описывает только то, что меняется:

В широком окне компьютера превью шире 720px, поэтому вы видите три колонки; строка под сеткой показывает ширину и число колонок. Сузьте окно, и их станет две при ширине меньше 720px и одна при ширине меньше 480px. Порядок двух запросов важен: на широком экране истинны оба, и побеждает более поздний, поэтому запросы min-width идут от меньшего к большему. (С запросами max-width наоборот: сначала больший.)

Как выбрать брейкпоинты

Официального списка нет. Вот частые значения и что они обычно разделяют:

БрейкпоинтОбычно
min-width: 640pxкрупные телефоны в горизонтальной ориентации, маленькие планшеты
min-width: 768pxпланшеты в вертикальной ориентации
min-width: 1024pxпланшеты в горизонтальной ориентации, небольшие ноутбуки
min-width: 1280pxкомпьютеры

Способ лучше: начните с узкой раскладки, расширяйте окно, пока что-то не начнет выглядеть плохо (слишком длинные строки, слишком широкие карточки), и поставьте брейкпоинт там. Большинству страниц хватает двух-трех брейкпоинтов. Часто они вообще не нужны: repeat(auto-fit, minmax(200px, 1fr)) в grid, flex-wrap и clamp() подстраиваются плавно.

Каждой странице нужен метатег viewport

Без этого тега в <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 не передает системную настройку во фрейм с sandbox, как этот. Кнопка задает те же переменные вручную, так работает переключатель темы на сайте. :root[data-theme] специфичнее простого :root внутри запроса, поэтому выбор читателя побеждает системный. Сохраните пример в файл и откройте его, чтобы увидеть, как запрос следует за системой.

Скрипт показывает вторую половину медиазапросов: matchMedia() выполняет тот же запрос из JavaScript, возвращает, срабатывает ли он сейчас, и вызывает событие change, когда ответ меняется. Используйте его, когда от экрана зависит поведение, а не только оформление, например меню на сенсорных устройствах открывается иначе.

С движением то же самое. Оборачивайте крупные или зацикленные анимации в проверку prefers-reduced-motion, как показано на странице про анимацию.

Контейнерные запросы: когда решает компонент

Медиазапрос знает только окно просмотра. Карточке, которая на одной странице стоит и в широкой основной колонке, и в узкой боковой панели, нужно знать собственное пространство. Это контейнерный запрос: отметьте родителя container-type: inline-size, а затем используйте @container с теми же условиями.

Потяните за угол серого блока, чтобы изменить его размер. Карточка меняет раскладку при ширине блока 360px, каким бы ни было окно:

При начальных 480px картинка стоит рядом с текстом. Сузьте блок меньше 376px (360px содержимого плюс по 8px padding с каждой стороны), и картинка переместится над текстом. Контейнерные запросы работают во всех актуальных основных браузерах.

Стили для печати

@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. Телефоны тогда сообщают широкое окно раскладки, и запросы для маленьких экранов никогда не срабатывают.
  • Запросы в неправильном порядке. Два истинных запроса с одним селектором: побеждает более поздний. Располагайте запросы 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?

Большинство сайтов пишут mobile first: базовый CSS для маленьких экранов, а запросы min-width добавляют раскладку по мере роста экрана. Запросы max-width работают наоборот, начиная с раскладки для компьютера. Подходит любой вариант; смешивание обоих на одном элементе приводит к перекрытиям и пропускам на границах.

Какие стандартные брейкпоинты для медиазапросов?

Официального стандарта нет. Частые значения около 640px, 768px, 1024px и 1280px, но правило лучше такое: добавляйте брейкпоинт там, где ваша раскладка начинает выглядеть плохо, и держите их число небольшим.

Почему медиазапрос не работает на телефоне?

Самая частая причина: нет метатега viewport. Без <meta name="viewport" content="width=device-width, initial-scale=1"> мобильные браузеры делают вид, что страница шириной около 980px, и запросы для маленькой ширины никогда не срабатывают. Также проверьте, что запрос стоит после правил, которые он переопределяет.

Как написать медиазапрос для темной темы?

Используйте @media (prefers-color-scheme: dark) { ... }. Он срабатывает, когда в операционной системе или браузере пользователя выбрана темная тема, и внутри можно переопределить цвета или CSS-переменные.

Чем медиазапросы отличаются от контейнерных запросов?

Медиазапрос проверяет окно просмотра (все окно). Контейнерный запрос, который пишется через @container, проверяет размер родительского элемента, отмеченного container-type: inline-size. Используйте контейнерные запросы для компонента, который на одной странице встречается и в широких, и в узких местах.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ