Медиазапрос CSS применяет блок стилей, только когда устройство или браузер соответствует условию: например, viewport не уже заданной ширины, экран в альбомной ориентации или включена тёмная тема. @media (min-width: 48rem) { ... } означает «применяй эти правила, когда ширина viewport не меньше 48rem». Именно медиазапросы позволяют одной странице менять раскладку на телефонах, планшетах и десктопах.
Базовый синтаксис
@media (min-width: 768px) {
.sidebar { display: block; }
}
У медиазапроса три необязательные части:
- тип носителя:
screen,printилиall(по умолчанию); - одна или несколько медиафункций в скобках, например
(min-width: 768px)или(orientation: landscape); - логические операторы, которые их объединяют:
and,,(или) иnot.
@media screen and (min-width: 768px) and (orientation: landscape) { ... }
@media (max-width: 480px), (orientation: portrait) { ... }
@media not print { ... }
Медиазапрос можно применить и ко всей таблице стилей: <link rel="stylesheet" href="print.css" media="print">.
min-width или max-width
min-width означает «такой ширины или шире», max-width — «такой ширины или уже». От выбранного направления зависит, как вы пишете остальной CSS.
- Mobile first (рекомендуется): базовые стили рассчитаны на телефоны, а запросы с
min-widthдобавляют раскладку для больших экранов. Подробнее в статье о подходе mobile first. - Desktop first: базовые стили рассчитаны на десктоп, а запросы с
max-widthотменяют лишнее для маленьких экранов.
/* Mobile first */
.nav { flex-direction: column; }
@media (min-width: 48rem) {
.nav { flex-direction: row; }
}
Если смешивать оба направления в одном проекте, появляются пересечения и дыры. Выберите одно, а второе используйте только для мелких исключений.
Современный синтаксис диапазонов
В Media Queries Level 4 появились операторы сравнения. Их проще читать, и с ними легче избежать ошибок на единицу:
@media (width >= 48rem) { ... }
@media (40rem <= width < 64rem) { ... }
Синтаксис диапазонов поддерживают все актуальные основные браузеры (Chrome и Edge с версии 104, Firefox с 63, Safari с 16.4). Если нужно поддерживать старые версии Safari, продолжайте писать min-width и max-width или поручите преобразование сборщику.
Учтите, что (width < 768px) и (max-width: 767px) не одно и то же: первый вариант срабатывает и на 767,5 px, а такая ширина бывает на экранах с дробной плотностью пикселей.
Медиазапросы для мобильных: типовые примеры
Колонки друг под другом на маленьких экранах:
.grid { display: grid; gap: 1rem; }
@media (min-width: 40rem) {
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 64rem) {
.grid { grid-template-columns: repeat(4, 1fr); }
}
Меньшие отступы на телефонах:
.section { padding: 2rem 1rem; }
@media (min-width: 64rem) {
.section { padding: 6rem 2rem; }
}
Телефон в альбомной ориентации с маленькой высотой:
@media (orientation: landscape) and (max-height: 30rem) {
.site-header { position: static; }
}
Про последний случай часто забывают. Липкая шапка, которая нормально выглядит в портретной ориентации, в альбомной может занять треть экрана.
Какие ширины выбирать — отдельный вопрос, он разобран в статье о брейкпоинтах для адаптивной вёрстки.
Медиафункции помимо ширины
| Функция | Пример | Для чего |
|---|---|---|
orientation |
(orientation: landscape) |
Изменить раскладку при повороте телефона или планшета |
hover |
(hover: hover) |
Эффекты наведения только на устройствах, где наведение возможно |
pointer |
(pointer: coarse) |
Крупные зоны нажатия на сенсорных экранах |
prefers-color-scheme |
(prefers-color-scheme: dark) |
Тёмная тема |
prefers-reduced-motion |
(prefers-reduced-motion: reduce) |
Отключение анимаций |
resolution |
(min-resolution: 2dppx) |
Картинки высокой плотности в CSS-фонах |
display-mode |
(display-mode: standalone) |
Стили для установленного веб-приложения |
Функции hover и pointer важнее, чем кажется. У сенсорного ноутбука или планшета с десктопной раскладкой всё равно нет наведения, поэтому меню, которые открываются только по hover, оборачивайте в @media (hover: hover).
Задавайте брейкпоинты в rem или em
Медиазапросы в rem или em учитывают размер шрифта, заданный пользователем в браузере. Если человек увеличит базовый размер текста с 16 px до 24 px, брейкпоинт 48rem сдвинется с 768 px на 1152 px, и при крупном тексте страница переключится на более просторную раскладку. Брейкпоинты в пикселях эту настройку игнорируют. Внутри медиазапросов и rem, и em отсчитываются от исходного размера шрифта браузера, а не от размера шрифта html, который вы задали в CSS.
Медиазапросы и container queries
Медиазапросы реагируют на viewport. Container queries реагируют на размер контейнера элемента:
.card-wrap { container-type: inline-size; }
@container (min-width: 30rem) {
.card { display: flex; }
}
Используйте медиазапросы для раскладки всей страницы (колонки, навигация), а container queries — для компонентов, которые встречаются в местах разной ширины.
Частые ошибки
- Нет метатега viewport. Без него телефоны сообщают ширину viewport около 980 px, и мобильные медиазапросы никогда не срабатывают. Подробнее: метатег viewport.
- Пересекающиеся диапазоны, например
max-width: 768pxиmin-width: 768px, которые оба срабатывают ровно на 768 px. - Медиазапрос стоит раньше базового правила. При одинаковой специфичности побеждает более позднее правило, поэтому запрос должен идти после того правила, которое он переопределяет.
- Проверка только изменением размера окна. Десктопные браузеры не сужаются до ширины большинства телефонов. Используйте режим устройства или Mobile View Extension, которое задаёт настоящий viewport и user agent, и прочитайте, как проверить адаптивность сайта.
Частые вопросы
Какой медиазапрос нужен для мобильных телефонов?
Единого запроса для телефонов нет. В CSS по принципу mobile first телефоны — это вариант по умолчанию без всякого запроса, а для больших экранов добавляется @media (min-width: 40rem) или похожий. Чтобы нацелиться только на маленькие экраны, используйте @media (max-width: 39.99rem) или (width < 40rem).
В чём задавать медиазапросы: px, em или rem?
rem или em лучше для доступности: тогда брейкпоинты масштабируются вместе с размером шрифта пользователя. px тоже работает, но эту настройку игнорирует.
Где размещать медиазапросы в CSS?
Либо рядом с компонентом, который они меняют, либо группой в конце файла. Рядом с компонентом поддерживать проще, но запрос всё равно должен идти после базовых правил.