Медиа-запросы в CSS: как сделать сайт адаптивным под телефон, планшет и ПК

Медиа-запрос включает CSS-стили только при нужном условии: ширине окна, ориентации экрана, тёмной теме в системе.

Показываем синтаксис @media, собираем сетку карточек на одну, две и три колонки с кадрами из Chrome, сравниваем брейкпоинты Bootstrap и Tailwind и разбираем пять ошибок, из-за которых запрос не срабатывает.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 2872 статьи автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3526 экспертных мнений Подписаться на Телеграм-канал
Обложка: Медиа-запросы в CSS: как сделать сайт адаптивным под телефон, планшет и ПК

Медиа-запрос в CSS — это правило @media, которое включает стили только при выполнении условия: ширина окна больше 768 пикселей, экран повёрнут горизонтально, в системе включена тёмная тема. Чтобы сайт перестраивался под телефон, добавьте в <head> метатег viewport, напишите базовые стили для узкого экрана и допишите блоки @media (min-width: 768px) { … } для широких.

Ниже разберём синтаксис, соберём адаптивную сетку карточек по шагам и покажем ошибки, из-за которых медиа-запрос не срабатывает. Если вы только начинаете, сначала загляните в разбор того, что такое CSS и как его подключать, и в обзор того, какие бывают виды вёрстки.

CheckroiCheckroiПодборка курсов по CSS182 курса • 37 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Как устроен медиа-запрос

Рой собирает адаптивную страницу на трёх экранах

Запись состоит из трёх частей: директива @media, условие и блок стилей. Стили внутри блока работают поверх обычных, пока условие истинно.

@media screen and (min-width: 768px) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}
  • Тип устройства: screen (экран), print (печать) или all. Его можно не писать: по умолчанию подставляется all.
  • Медиафункция в круглых скобках. Это характеристика, которую проверяет браузер: min-width, orientation, prefers-color-scheme.
  • Логические операторы склеивают условия в одно выражение.

Операторов четыре, и путают их чаще всего запятую с and:

Оператор Что делает Пример
and все условия должны выполниться (min-width: 768px) and (orientation: landscape)
запятая хватит любого условия из списка (orientation: portrait), (min-width: 1024px)
not переворачивает весь запрос not print
only прячет запрос от очень старых браузеров, на современные не влияет only screen and (min-width: 768px)

Современные браузеры понимают и запись диапазоном из спецификации Media Queries Level 4. Она короче и не оставляет дыр на границе:

/* старая запись */
@media (min-width: 768px) and (max-width: 1199.98px) { … }

/* новая запись */
@media (768px <= width < 1200px) { … }

По данным справочника MDN, диапазоны поддерживают Chrome с версии 104, Firefox с 102 и Safari с 16.4. Если проект должен открываться на старых iPhone, оставьте пары min-width и max-width.

Пошаговая инструкция: адаптивная сетка карточек

Соберём типичный блок: на телефоне карточки идут в одну колонку, на планшете в две, на широком мониторе в три. Код ниже мы проверили в Chrome 154 на ширине 375, 767, 768, 1199 и 1200 пикселей.

  1. Добавьте метатег viewport в <head>. Без него телефон рисует страницу шириной около 980 пикселей и уменьшает её, и запросы под мобильные не сработают:
    <meta name="viewport" content="width=device-width, initial-scale=1">
  2. Напишите базовые стили для самого узкого экрана. Это одна колонка без всяких условий:
    .cards {
      display: grid;
      grid-template-columns: 1fr;
      gap: 16px;
    }
  3. Добавьте запрос для планшета. С 768 пикселей карточки встают по две:
    @media (min-width: 768px) {
      .cards { grid-template-columns: repeat(2, 1fr); }
    }
  4. Добавьте запрос для десктопа ниже предыдущего. Порядок важен: при одинаковой специфичности побеждает правило, которое стоит в файле позже:
    @media (min-width: 1200px) {
      .cards { grid-template-columns: repeat(3, 1fr); }
    }
  5. Проверьте результат. В Chrome откройте инструменты разработчика (F12 в Windows, Cmd+Option+I в macOS) и включите режим устройств: Ctrl+Shift+M или Cmd+Shift+M. Тяните край окна и следите, на какой ширине меняется сетка.

Так выглядит та же страница на трёх ширинах. В правом верхнем углу плашка показывает ширину окна и число колонок, которое насчитал браузер.

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

На 820 пикселях сработал первый запрос: сетка стала двухколоночной, а вместо значка меню появились ссылки. Меню переключается тем же блоком @media (min-width: 768px), в нём просто больше правил.

Та же страница на ширине 820 пикселей: медиа-запрос min-width 768px перестроил сетку в две колонки
С 768 пикселей срабатывает первый медиа-запрос: две колонки и ссылки меню вместо значка

С 1200 пикселей включается второй запрос, и карточки встают по три. Ширину колонок под каждый экран обычно берут из макета: как его подготовить, рассказываем в статье о том, как сделать макет сайта самому.

Страница на ширине 1280 пикселей: медиа-запрос min-width 1200px выстроил карточки в три колонки
С 1200 пикселей добавляется второй запрос, и карточки встают по три в ряд
Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Mobile first или desktop first и какие брейкпоинты брать

Телефон, планшет и ноутбук выстроены по ширине

Направление определяет, какие условия вы пишете. При mobile first базовые стили рассчитаны на телефон, а запросы с min-width добавляют правила для экранов шире. При desktop first наоборот: за основу берут десктоп и урезают его запросами с max-width.

Mobile first Desktop first
Условия min-width, по возрастанию max-width, по убыванию
Что получает телефон только базовые стили, без переопределений десктопные стили плюс правки поверх них
Когда удобно новый проект, большая доля мобильного трафика адаптация готового десктопного сайта

Для новых проектов чаще выбирают mobile first: кода на телефоне меньше, а усложнять макет проще, чем упрощать. Тот же подход заложен в Bootstrap и Tailwind: их классы вида col-md-6 и md:flex срабатывают от указанной ширины и выше.

Макеты под телефон и десктоп обычно рисует веб-дизайнер, а верстальщик решает, на какой ширине один вариант сменяет другой.

Брейкпоинты, то есть ширины переключения, у популярных фреймворков такие:

Фреймворк Брейкпоинты, px Источник
Bootstrap 5.3 576, 768, 992, 1200, 1400 документация Bootstrap
Tailwind CSS 4 640, 768, 1024, 1280, 1536 (в rem: 40, 48, 64, 80, 96) документация Tailwind

Обе сетки сходятся на 768 пикселях, поэтому эта граница почти всегда отделяет телефон от планшета. Остальные лучше ставить по контенту: сузьте окно и найдите ширину, на которой строка меню перестала помещаться или таблица вылезла за край. О том, как вести себя с широкими таблицами, есть отдельный раздел в инструкции про таблицы в HTML.

Медиафункции, которые пригодятся кроме ширины

Ширина окна закрывает большую часть задач, но браузер умеет проверять и другие вещи. Полный список ведёт руководство MDN по медиавыражениям, а в работе обычно хватает этих:

Медиафункция Что проверяет Зачем
orientation: landscape окно шире, чем выше галерея и видео на повёрнутом телефоне
hover: hover и pointer: fine есть мышь или тачпад эффекты при наведении только там, где их можно увидеть
prefers-color-scheme: dark в системе включена тёмная тема тёмная версия сайта без переключателя
prefers-reduced-motion: reduce пользователь просил меньше анимации отключить параллакс и автопрокрутку
min-resolution: 2dppx плотный экран, как у Retina подставить фон в двойном разрешении
print (тип устройства) страницу печатают спрятать меню и рекламу при печати

На телефоне с сенсорным экраном условие (hover: hover) and (pointer: fine) не выполняется, мы проверили это в эмуляции Chrome. Поэтому выпадающее меню, которое открывается только при наведении, на таком устройстве нужно дублировать открытием по нажатию.

Тёмная тема и уменьшение анимации пишутся так же, как запрос по ширине:

@media (prefers-color-scheme: dark) {
  body { background: #16141f; color: #ece9f6; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none; transition: none; }
}

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

Запрос print выручает, когда статью или прайс распечатывают. Меню, кнопки и баннеры на бумаге не нужны, а у гиперссылок полезно напечатать адрес прямо в тексте:

@media print {
  header, nav, .banner { display: none; }
  a[href^="http"]::after { content: " (" attr(href) ")"; }
}

Проверить печатную версию без принтера можно в DevTools: меню из трёх точек → More tools → Rendering → Emulate CSS media type → print.

Медиа-запросы в HTML и JavaScript

Условие можно поставить и вне CSS-файла. Атрибут media есть у <link>: такой файл стилей применится только на подходящем экране. Браузер всё равно скачает его, просто с низким приоритетом.

<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="wide.css" media="(min-width: 1200px)">

У элемента <picture> атрибут media выбирает картинку. На телефоне загрузится узкий кадр, на десктопе широкий:

<picture>
  <source media="(min-width: 768px)" srcset="hero-wide.jpg">
  <img src="hero-narrow.jpg" alt="Главный экран">
</picture>

В JavaScript то же условие проверяет метод matchMedia. Он возвращает объект со свойством matches и сообщает о смене состояния событием change. Это полезно, когда меню собирается скриптом и при повороте телефона его надо пересобрать. Как скрипт находит и меняет элементы страницы, мы разбирали в статье про DOM-дерево в JavaScript.

const tablet = window.matchMedia('(min-width: 768px)');

function render(e) {
  console.log(e.matches ? 'две колонки' : 'одна колонка');
}

render(tablet);
tablet.addEventListener('change', render);

Мы проверили этот код в Chrome: при ширине 375 пикселей в консоли «одна колонка», после растягивания окна до 900 пикселей событие сработало и вывело «две колонки», после сужения до 500 снова «одна колонка».

Когда @media не нужен

Две современные техники закрывают часть задач без медиа-запросов.

  • Контейнерные запросы @container проверяют ширину родительского блока. Карточка в узкой боковой колонке и та же карточка в широкой ленте получают разную раскладку из одного CSS. По руководству MDN (страница на английском), они работают в Chrome с 105, Safari с 16 и Firefox с 110.
  • Функция clamp() плавно меняет размер шрифта вместе с шириной экрана, без ступенек на брейкпоинтах.
.wrap { container-type: inline-size; }

@container (min-width: 400px) {
  .card { display: flex; }
}

h1 { font-size: clamp(1rem, 0.75rem + 1.2vw, 1.5rem); }

В нашей проверке заголовок с таким clamp() занимал 16,5 пикселя на ширине 375, 21,2 на 767 и упирался в потолок 24 пикселя с 1000 пикселей. Карточка внутри контейнера шириной 300 пикселей осталась блоком, в контейнере на 500 пикселей стала flex-строкой при одинаковом окне.

Частые ошибки и что с ними делать

Рой нашёл пропущенную строку в коде страницы

Почти всё, на чём ломаются медиа-запросы, мы воспроизвели в Chrome. Пять случаев встречаются чаще прочих.

  1. На телефоне ничего не меняется. Причина почти всегда в отсутствии метатега viewport. Без него окно эмулированного телефона шириной 375 пикселей сообщило странице 981, и сработал планшетный запрос. Решение: строка из первого шага инструкции.
  2. На ширине ровно 768 пикселей вёрстка ведёт себя странно. Запросы (max-width: 768px) и (min-width: 768px) выполняются одновременно, и побеждает тот, что ниже в файле. Решение: max-width: 767.98px или запись диапазоном width < 768px.
  3. Запрос не работает вообще. Проверьте пробел между and и скобкой. Запись screen and(min-width: 768px) браузер считает ошибкой и выбрасывает весь блок.
  4. Правило внутри запроса не применяется, хотя условие верное. Медиа-запрос не прибавляет специфичности. Селектор .menu .link вне запроса перебьёт .link внутри него. Решение: тот же селектор в обоих местах.
  5. Десктопные стили затирают мобильные. При mobile first запросы с min-width должны идти по возрастанию, при desktop first запросы с max-width по убыванию. Перемешанный порядок даёт результат, который зависит от того, какой блок оказался последним.

Решение для отладки: в DevTools на вкладке Elements выберите элемент и посмотрите панель Styles. Сработавшие запросы показаны над правилами, а перечёркнутые свойства подскажут, кто их перебил.

Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Где освоить вёрстку системно

Медиа-запросы проходят на любом курсе вёрстки вместе с Flexbox, Grid и подготовкой макета. Если хотите разобраться в адаптиве целиком, загляните в подборку курсов по навыку адаптивная вёрстка или в более широкий каталог курсов по CSS.

Чем такой специалист занимается каждый день и сколько получает, мы рассказали в статье о профессии HTML-верстальщика. Если вёрстка для вас первый шаг к JavaScript и фреймворкам, пригодится план как стать фронтенд-разработчиком.

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
HTML и CSS с нуля
Перейти на сайт курса
SkillboxSkillbox91 626 ₽7635 ₽/мес.11 месяцевОбзор курса
HTML и CSS. Адаптивная вёрстка и автоматизация
Перейти на сайт курса
HTMLAcademyHTML Academy23 400 ₽3900 ₽/мес.2 месяцаОбзор курса
Основы вёрстки HTML и CSS
Перейти на сайт курса
Академия СинергияАкадемия Синергия32 676 ₽2723 ₽/мес.3 месяцаОбзор курса
HTML и CSS с нуляSkyproSkypro195 160 ₽7972 ₽/мес.12 месяцевОбзор курса
Frontend-разработчик: сайты на HTML/CSS/JavaScript
Перейти на сайт курса
CoddyCoddy9200 ₽ - 3 месяцаОбзор курса

Больше программ — в полном каталоге курсов по CSS

Часто задаваемые вопросы

Что такое медиа-запросы в CSS?

Это правило @media с условием: стили внутри него работают, только когда условие выполняется. Чаще всего проверяют ширину окна, например @media (min-width: 768px), но можно проверять ориентацию экрана, тёмную тему в системе или режим печати.

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

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

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

Общая граница почти у всех 768 пикселей: от неё начинается планшет. Bootstrap 5.3 использует 576, 768, 992, 1200 и 1400 пикселей, Tailwind CSS 4 использует 640, 768, 1024, 1280 и 1536. Остальные точки лучше ставить там, где ломается ваш макет.

Чем min-width отличается от max-width в медиа-запросе?

min-width: 768px срабатывает на ширине 768 пикселей и больше, max-width: 768px на 768 и меньше. На самой границе выполняются оба, поэтому для desktop first пишут max-width: 767.98px или новую запись width < 768px.

Можно ли проверить медиа-запрос в JavaScript?

Да, методом window.matchMedia(). Он принимает то же условие, что и CSS, возвращает объект со свойством matches и сообщает о смене ширины событием change. Как скрипт меняет элементы страницы, мы разбирали в статье про DOM-дерево.

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

@media проверяет окно браузера, @container проверяет ширину родительского блока. Так одна и та же карточка перестраивается и в узкой боковой колонке, и в широкой ленте. Контейнерные запросы работают в Chrome с версии 105, Safari с 16 и Firefox с 110.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!