Адаптивность: медиазапросы и контейнерные запросы

Зачем:Сайт открывают на телефоне, ноутбуке и большом мониторе, причём чаще всего на телефоне. После урока вы делаете mobile first, ставите точки перелома по содержимому и используете контейнерные запросы, чтобы компонент подстраивался под своё место.

30 минут

Озвучено синтезом речи (Qwen3-TTS), голос не принадлежит человеку.

Адаптивный дизайн — один сайт, который подстраивается под любой экран медиазапросами. Контейнерные запросы подстраивают компонент под ширину его родителя.

Один сайт для всех экранов

В 2007 году вышел iPhone, и сайты, которые прекрасно выглядели на ноутбуке, вдруг оказались на экране в несколько раз меньше. Сначала делали отдельный мобильный сайт на другом адресе: m.example.ru, и вели две копии. Это было дорого и путало. В 2010 году американский дизайнер Итан Маркотт описал другой путь и назвал его адаптивным веб-дизайном: один сайт, один HTML, а вид подстраивается под экран. Он состоит из трёх вещей:

  1. резиновая сетка: ширины колонок в долях, а не в пикселях;
  2. гибкие изображения это картинки сжимаются вместе с колонкой: img { max-width: 100%; height: auto; };
  3. медиазапросы: правила, которые включаются на определённой ширине.

К этому добавились два соглашения. Viewport: строка <meta name="viewport" content="width=device-width, initial-scale=1">, о которой вы знаете из урока про head. Без неё телефон уменьшает страницу, как «широкий экран», и медиазапросы не срабатывают. И mobile first: идея Люка Вроблевски (2009–2011): начинайте не с настольного вида, а с мобильного.

Mobile first и медиазапросы

Mobile first на практике: основные правила пишутся для узкого экрана, и для широкого к ним добавляют:

/* для всех: один столбец */
.layout {
  display: grid;
  grid-template-areas: "main" "aside";
  gap: 2rem;
}

/* от 48rem (768px) и шире: добавляем два столбца */
@media (min-width: 48rem) {
  .layout {
    grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
    grid-template-areas: "main aside";
  }
}

Почему так, а не наоборот? Телефону достаётся простой код, и ему не надо ничего отменять, а усложнение приходит только на экраны, которым оно по силам. Условие min-width читается «шире или равно».

Другие правила: точки перелома выбирают по содержимому, а не по устройствам. Потяните окно вширь и там, где строка становится слишком длинной, а карточки слишком растянутыми, поставьте точку. Моделей телефонов тысячи и они меняются, а ваш контент остаётся. Размеры пишите в rem: тогда точки уважают настройку шрифта читателя. И не забывайте, что адаптивность это не только ширина: есть высота, ориентация, а ещё настройки человека (prefers-color-scheme, prefers-reduced-motion), о которых в следующих уроках.

Попробуйте сами: двигайте ширину и смотрите, что происходит в этой странице. Внутри рамки настоящие медиазапросы.

Одна страница на разных экранах
  • Медиазапрос min-width: 48rem (768 px): не сработал, один столбец.
  • Карточка в основном блоке: контейнер около 358 px, а порог контейнерного запроса 416 px (26rem): вертикальная.
  • Карточка сбоку: контейнер около 358 px: вертикальная.

Окно одно, а карточки ведут себя по-разному: каждая смотрит на ширину своего места. В этом разница между медиазапросом и контейнерным запросом.

Проверьте себя: почему в mobile first пишут min-width, а не max-width?

Основные правила для узкого экрана, а min-width добавляет усложнение там, где места больше. Телефону ничего не приходится отменять.

Контейнерные запросы: подстраиваемся под своё место

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

В 2022–2023 годах во всех браузерах появились контейнерные запросы: компонент смотрит на ширину своего родителя.

.cards {
  container-type: inline-size;
}

@container (min-width: 40rem) {
  .card {
    display: grid;
    grid-template-columns: 1fr auto;
  }
}

Родителю объявляют, что он «контейнер» (container-type: inline-size, по ширине), а потомок спрашивает: «а контейнер шире 40rem?». Если да, карточка раскладывается в две колонки, если нет, остаётся вертикальной. Теперь карточку можно поставить куда угодно, и она сама выберет вид. Посмотрите на схему выше: там две одинаковые карточки в одном окне, и ведут они себя по-разному.

Заметьте: контейнер не может быть тем же элементом, который меняется. Правила внутри @container действуют на потомков контейнера, а не на него самого.

Проверьте себя: чем контейнерный запрос лучше медиазапроса для карточки?

Он смотрит на ширину своего места, а не окна, поэтому одна и та же карточка в широкой и узкой колонках одного окна выбирает свой вид.

Пальцы и глаза

Адаптивность это ещё и удобство на ощупь. Кнопка должна быть достаточно большой для пальца: около 44 на 44 пикселя (min-height: 2.75rem): столько рекомендует Apple и стандарт WCAG уровня AAA; минимум в WCAG 2.2 для уровня AA это 24 пикселя. Между кнопками нужен зазор. Главное действие вашего проекта должно быть видно без прокрутки на экране телефона: для этого у вас есть кнопка вверху страницы.

Практика

Сделайте страницу адаптивной. В предпросмотре есть ширина, и вы можете посмотреть, что получается, но надёжнее другой способ: откройте инструменты разработчика (F12) и включите режим устройств (значок телефона, Ctrl+Shift+M). Там можно выбрать телефон и потянуть ширину от 320 пикселей.

Предупреждение: проверки в упражнении видят вашу страницу при одной ширине, поэтому они не смогут сказать, написали ли вы @media и в какую сторону. Они проверят то, что видно на любой ширине: viewport, гибкие изображения, размер кнопки и контейнер карточек. За медиазапросом следите сами, потянув окно.

Что было не так с 2014 годом

В 2014-м «адаптивность» часто означала «ужать десктопный вид»: колонки столбиком, меню в «гамбургер», а лишнее прячем display: none. Текст всё равно оставался слишком мелким, а картинки грузились в полном размере. Привычка писать «десктоп сначала, а потом всё отменять» дала миру тысячи страниц, где телефон разбирает правила, которые тут же отменяются.

Практика

Готовлю вашу страницу…

Тест

Вопрос 1 из 6

Из чего состоит адаптивная вёрстка в определении Итана Маркотта (2010)?

Несколько ответов

Карточки

Эти карточки попадут в повторение, когда вы завершите урок.

  • Три составляющие адаптивной вёрстки?

    Ответ

    Резиновая сетка (доли, а не пиксели), гибкие изображения (max-width: 100%) и медиазапросы. Термин ввёл Итан Маркотт в 2010 году.

  • Что такое mobile first?

    Ответ

    Базовые стили пишут для узкого экрана, а для широкого добавляют @media (min-width: …). Телефону достаётся простой код, и ничего не надо отменять.

  • Как ставить точки перелома?

    Ответ

    По содержимому: где макет начинает ломаться, там и точка. Не по моделям телефонов. Пишите в rem, чтобы они уважали размер шрифта читателя.

  • Что такое контейнерный запрос?

    Ответ

    container-type: inline-size у родителя и @container (min-width: …) у потомка: компонент подстраивается под ширину своего места, а не окна.

  • Какой размер у кнопки для пальца?

    Ответ

    Около 44 × 44 пикселей (2.75rem): так рекомендуют Apple и WCAG 2.5.5. Минимум по WCAG 2.2 уровня AA 24 пикселя.

Источники

Дальше

Теперь вы знаете: адаптивная вёрстка и mobile first, медиазапросы, контейнерные запросы.