Медиазапрос на одном примере
Медиазапрос оборачивает 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) |
orientation | portrait (выше, чем шире) или 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. Используйте контейнерные запросы для компонента, который на одной странице встречается и в широких, и в узких местах.