Медиа- и контейнерные запросы

Что это такое

Запрос оборачивает правила CSS в условие, и они применяются лишь тогда, когда в окружении истинно что-то одно. Медиа-запросы спрашивают про окно и устройство: его ширину, цветовую схему, попросил ли пользователь уменьшить движение. Контейнерные запросы спрашивают про собственный размер элемента-предка, и это позволяет компоненту адаптироваться к данному ему пространству, а не к экрану, на котором он случайно оказался.

Почему это важно

Одна раскладка не может обслужить телефон, ноутбук и боковую панель в 300 пикселей. Запросы — то, чем одна таблица стилей покрывает все три без трёх таблиц, и то, что делает адаптивный дизайн техникой, а не набором отдельных сайтов. Ловушка в другом: считать их определением устройства. Точка перелома на 768px перестаёт что-либо значить, когда телефон так же широк, как планшет, а настольный виджет так же узок, как телефон. Однако запросы, написанные под содержимое, а не под железо, переживают новые устройства, и трогать их не приходится.

Ключевые идеи

Спрашивайте про условие, а не про устройство

Пишите запрос там, где раскладка действительно ломается. Сужайте окно, пока строка текста не станет нечитаемой или ряд карточек не переполнится, отметьте ширину и поставьте запрос туда. Класс с именем tablet стареет плохо; комментарий «два столбца перестают помещаться» — нет.

.grid {
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
  .grid { grid-template-columns: 1fr 1fr; }
}

Синтаксис диапазонов делает замысел читаемым

Старая пара min-width/max-width легко читается в одну сторону и неловка в обе. Современный синтаксис диапазонов говорит то, что значит, в одном условии, и это важно, когда несколько запросов перекрываются.

@media (48rem <= width < 80rem) {
  .sidebar { display: block; }
}

Начинайте малым и добавляйте

Если основа таблицы стилей — узкая раскладка, а возможности добавляют через запросы min-width, файл сначала работает, а потом улучшается; порядок каскада при этом совпадает с порядком чтения. Однако альтернатива — настольная раскладка, снимаемая слой за слоем запросами max-width, — вынуждена отменять объявления, а это почти всегда значит !important или продублированные блоки.

Контейнерные запросы дают компоненту собственные точки перелома

Объявите контекст сдерживания через container-type: inline-size на обёртке, назовите его и запрашивайте по имени. Тогда один и тот же компонент-карточка может стоять и в широкой основной колонке, и в узкой боковой панели, адаптируясь правильно в обеих.

.card-grid { container-type: inline-size; container-name: cards; }

@container cards (min-width: 30rem) {
  .card { grid-template-columns: 8rem 1fr; }
}

Спрашивайте и о предпочтениях пользователя

prefers-reduced-motion, prefers-color-scheme и prefers-contrast — такие же медиа-запросы; именно так дизайн уважает настройки, о которых операционная система уже спросила пользователя. Относитесь к ним как к части раскладки, а не как к необязательному улучшению, прикрученному в конце.

Практика

Выведите три точки перелома из содержимого

Возьмите страницу с шапкой, основной колонкой и боковой панелью. Медленно уменьшайте ширину окна и записывайте каждую ширину, на которой раскладка перестаёт работать. Напишите запросы только на этих ширинах.

Готово, когда: на странице нет горизонтальной прокрутки и нигде не накладывается текст ни на одной ширине от 320px вверх, и каждый запрос несёт комментарий о том, что ломается ниже него.

Заставьте карточку работать в двух контейнерах

Постройте один стиль карточки и поставьте её в широкую колонку и в узкую боковую панель. Дайте обёртке контекст контейнера и перенесите правило карточки об одном столбце в запрос @container.

Готово, когда: карточка переключается на компактную раскладку в боковой панели без смены каких-либо классов, и вы проверили это, меняя ширину панели, а не окна.

Частые ошибки

  • Выбор точек перелома из списка устройств — запросы устаревают, и раскладка ломается на следующем классе устройств; исправить: выводите каждую точку перелома из того, где содержимое действительно переполняется.
  • Правила max-width от настольного вида — каждый шаг сужения обязан отменять широкую раскладку, и файл удваивается; исправить: начинайте с узкой раскладки и добавляйте через min-width.
  • Забытый метатег viewport — телефон отрисовывает страницу в 980px, сжатую под экран, и каждый запрос видит неверную ширину; исправить: включите объявление viewport в head документа.
  • Контейнерный запрос без контекста сдерживания — у запроса нет зарегистрированного контейнера, и он молча никогда не срабатывает; исправить: задайте container-type и container-name на обёртке.
  • prefers-reduced-motion как необязательное — чувствительные к движению пользователи не могут отказаться, и анимация всё равно играет; исправить: оберните необязательные переходы в запрос no-preference.

Что читать дальше

  • Flexbox и grid (02-frontend/05-css/02-flexbox-and-grid) — запросы меняют, какая раскладка применяется; саму раскладку всё ещё нужно выбрать.
  • Современные возможности CSS (02-frontend/05-css/04-modern-css) — логические свойства и clamp() убирают несколько запросов, которые иначе пришлось бы писать.
  • Адаптивный веб-дизайн (02-frontend/10-responsive-web-design) — более широкая дисциплина, к которой относится эта техника.