EverProduct
Фронтенд

Этап 02 · Страница, которая держит удар

Вода в стакане: одна страница, любой экран

Страница адаптивна по умолчанию — чтобы её сломать, нужно постараться. Бо́льшая часть навыка — не добавить хитростей, а убрать допущения про экран, который у тебя случайно оказался.

Сайт великолепно смотрится на твоём мониторе. Кто-то открывает его с телефона: текст нечитаемо мелкий, одна карточка торчит за край, а внизу горизонтальная полоса прокрутки — верный признак, что внутри что-то шире мира, в котором оно живёт.

Заметь, что произошло. Ты не забыл добавить адаптивность. Голый HTML-документ вообще без CSS уже работает на любой ширине: текст перетекает, ничего не вылезает, всё помещается. Чтобы это сломать, пришлось написать CSS. Где-то в твоих стилях лежит число, снятое с экрана, который стоит перед тобой.

Адаптивность — не функция, которую добавляют в конце. Это то, что страница делает, пока ты ей не помешал.

Строка, без которой не работает ничего

У мобильных браузеров есть защитная привычка: считая, что страница сделана под десктоп, они рисуют её шириной около 980 пикселей и уменьшают результат, чтобы старые сайты хотя бы читались. Твоя аккуратно собранная вёрстка получает то же обращение — отъезжает и становится крошечной.

Одна строка в шапке это отключает и велит браузеру использовать настоящую ширину устройства в настоящем масштабе. Это тег meta с именем viewport и содержимым width=device-width, initial-scale=1. Без него любой написанный тобой медиазапрос меряет вымысел. Это первое, что стоит проверить, когда сайт «сломан на мобильном» необъяснимым образом.

Начинай с узкого

Общепринятый подход — mobile-first: базовые стили пишутся для узкого случая, а медиазапросы min-width добавляют сложность по мере появления места.

Не мода, а три конкретные причины. Узкий случай — трудный, и решая трудный первым, ты не обнаружишь в конце, что десктопную вёрстку невозможно ужать. Узкая раскладка обычно простая — одна колонка, — поэтому из неё выходит база с меньшим количеством переопределений. И улучшения складываются: на 700 пикселях ты добавляешь вторую колонку, а не начинаешь с широкого и что-то отменяешь.

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

Откуда берутся точки перелома

Не из списка ширин устройств. Этот список устарел ещё в момент написания, а гоняться за ним — значит проектировать под железо вместо содержимого.

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

Обычно их выходит две-три, а не восемь. Куча точек перелома — признак того, что фиксированные размеры латают на каждой ширине вместо того, чтобы один раз сделать их гибкими.

Единицы — вот где настоящий навык

Бо́льшая часть адаптивности решается выбором единицы задолго до любого медиазапроса:

  • px — фиксированные. Уместны для волосяных рамок и мелких неизменных деталей. Неуместны для размера шрифта: они игнорируют настройку размера текста в браузере, на которую люди со слабым зрением реально опираются.
  • rem — относительно корневого размера шрифта. Выбор по умолчанию для текста и отступов: масштабируется вместе с настройкой пользователя и предсказуем, потому что корень не меняется от вложенности.
  • em — относительно размера шрифта текущего элемента. Полезно внутри компонента, чьи отступы должны расти вместе с его собственным текстом; коварно при вложенности, потому что перемножается.
  • % — относительно соответствующего размера родителя.
  • vw / vh — сотая доля ширины или высоты окна. Хороши для полноэкранных секций, а vh — известная ловушка на мобильных, где панели самого браузера то появляются, то исчезают. Более новая dvh это учитывает.
  • ch — ширина символа «0». max-width: 65ch — самая полезная одиночная строка типографического CSS: она ограничивает колонку текста комфортной длиной строки в том шрифте, который реально применился.

И одна функция, которая убирает бо́льшую часть точек перелома: clamp(минимум, желаемое, максимум). Заголовок с clamp(1.75rem, 4vw, 3rem) плавно растёт вместе с окном между двумя пределами вместо скачка на брейкпоинте. Текучесть вместо ступенек, в одной декларации.

Контейнерные запросы: настоящая единица адаптации

Медиазапрос спрашивает про окно. Но карточке нет дела до окна — ей важно, сколько места дали ей. Одна и та же карточка может стоять в узком сайдбаре и в широкой сетке на одной и той же странице, а медиазапрос эти два случая не различает.

Контейнерные запросы решают ровно это: ты помечаешь элемент как контейнер, и его дети получают возможность реагировать на его ширину. Компонент становится по-настоящему переносимым — он подстраивается там, куда его положили, ничего не зная про страницу вокруг. Если ты учишь фронтенд сейчас, изучай их вместе с медиазапросами, а не после: это тот инструмент, который старый всё это время подменял.

Картинки — обычный виновник

Картинки ломают адаптивность чаще, чем вёрстка.

max-width: 100% вместе с height: auto для всех изображений — базовая защита от большинства вылезаний. Дальше: srcset с sizes позволяет браузеру выбрать файл подходящего размера, а не отправлять на телефон фотографию шириной 3000 пикселей, — это решение и про трафик, и про вёрстку. aspect-ratio резервирует нужное место до того, как файл приехал, и страница перестаёт прыгать. А loading="lazy" для картинок ниже первого экрана откладывает то, до чего ещё никто не долистал.

Шире, чем ширина

Адаптивность — это подстройка под условия человека, и окно лишь одно из них. Современный CSS позволяет отреагировать на несколько других, каждое одним медиазапросом: prefers-color-scheme для тёмной темы, prefers-reduced-motion для тех, кого от анимаций укачивает — для них декоративный параллакс это физический симптом, а не вкусовщина, — и prefers-contrast.

Ещё два условия решаются не запросами, а единицами. Тот, кто выставил себе крупный шрифт по умолчанию, получит его только если ты задавал размеры в rem. А тот, кто увеличивает масштаб до 200% — а это очень частая история, — получит пригодную страницу, только если ничто не приколочено к фиксированным пиксельным высотам.

И ввод. Палец — не мышь: цели для касания хотят быть около 44 пикселей, а всё, что появляется только по наведению, на телефоне невидимо: меню на hover без тап-эквивалента — один из самых распространённых сломанных паттернов в вебе.

На практике

Собирай при узком окне. Не в конце — с первой минуты. Держи браузер шириной с телефон, пока работаешь, и расширяй, чтобы проверить. Это переворачивает стандартный провал, где десктопная версия готова, а мобильная превращается в спор с самим собой.

Сделай расширение окна привычкой с якорем. Правило привычек из сферы «Как учиться»: сигнал важнее намерения — привязывай к тому, что и так происходит. Закончил компонент — потянул окно. Каждый раз, пока не перестанешь это решать.

Охоться на вылезание. Появилась горизонтальная прокрутка — значит, что-то шире окна. Инструменты разработчика это находят; виновник почти всегда фиксированная ширина, длинная неразрывная строка или отрицательный отступ.

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

Ставь max-width в ch каждой текстовой колонке. Одна строка, и твоя типографика перестаёт быть нечитаемой на широких экранах.

Увеличь масштаб браузера до 200% и попользуйся страницей. Две минуты — и находятся все фиксированные высоты, о которых ты не помнил.

Проверь себя

Закрой статью и ответь своими словами:

  1. Почему страница без CSS уже адаптивна и что её ломает?
  2. Что делает мета-тег viewport и что происходит без него?
  3. Приведи три причины писать mobile-first, а не desktop-first.
  4. Откуда на самом деле берётся точка перелома?
  5. Почему размер шрифта стоит задавать в rem, а не в px?
  6. Что умеет контейнерный запрос такого, чего не умеет медиазапрос?
  7. Назови три условия помимо ширины окна, под которые должна подстраиваться страница.

Коротко

  • Документ адаптивен по умолчанию; работа над адаптивностью — в основном снятие фиксированных допущений, которые ты сам и добавил.
  • Мета-тег viewport — обязательное условие: без него мобильный браузер рисует вымысел, а твои медиазапросы меряют этот вымысел.
  • Mobile-first, потому что узкий случай трудный, база простая, а улучшения складываются, а не отменяются.
  • Точки перелома берутся из наблюдения за тем, как ломается твоё содержимое при перетаскивании окна, а не из списка размеров устройств.
  • Бо́льшую часть решают единицы: rem для текста и отступов, ch для ширины чтения, dvh вместо vh на мобильных и clamp() вместо ступенек.
  • Контейнерные запросы позволяют компоненту подстраиваться под выданное ему место — именно это делает его переносимым.
  • Картинкам нужны max-width: 100%, srcset, aspect-ratio и ленивая загрузка, иначе они ломают и вёрстку, и трафик.
  • Подстраивайся не только под ширину: цветовая схема, уменьшенная анимация, крупный шрифт по умолчанию, масштаб 200% и пальцы вместо мыши.