22 Модуль 5 Живой современный сайт 16 мин урок 22 из 25

Адаптивность: один сайт для всех экранов

Мета-viewport, подход mobile-first, медиазапросы по ширине и по предпочтениям человека, clamp() и container queries.

Что ты научишься делать

  • Сделаешь так, чтобы сайт не был мелким на телефоне
  • Напишешь медиазапросы, которые добавляют, а не чинят
  • Уберёшь половину медиазапросов за счёт гибких значений

#Первая строка адаптивности

Без специальной инструкции мобильный браузер притворяется, что экран шириной около 980 пикселей, и уменьшает страницу целиком. Сайт «помещается», но текст становится нечитаемым.

Одна строка в head это выключает.

HTMLБез неё ничего не заработает
<meta name="viewport" content="width=device-width, initial-scale=1">

width=device-width означает «считай ширину экрана настоящей», initial-scale=1 — «не масштабируй при загрузке». Мы писали эту строку ещё во втором уроке, теперь понятно зачем.

#Mobile-first: сначала узкий экран

Есть два подхода. Можно написать стили для широкого экрана, а потом медиазапросами всё «чинить» для узкого. А можно наоборот: написать базовые стили для телефона, а затем добавлять для больших экранов.

Второй путь удобнее. На телефоне раскладка обычно простая — одна колонка. Значит, базовые стили получаются короткими, а медиазапросы только усложняют картину по мере роста экрана.

Так не надо

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 900px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 600px) {
  .grid { grid-template-columns: 1fr; }
}

Сначала сложное, потом два запроса на упрощение. Каждое новое требование добавляет ещё одно исключение.

Так надо

.grid {
  display: grid;
  gap: 16px;
}

@media (min-width: 600px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .grid { grid-template-columns: repeat(4, 1fr); }
}

База — одна колонка, дальше только добавление. Читается сверху вниз как рассказ о том, как растёт экран.

#Медиазапросы

CSSСинтаксис
/* Экран шире 768 пикселей */@media (min-width: 768px) {  .sidebar { display: block; }}/* Диапазон */@media (min-width: 768px) and (max-width: 1199px) {  .card { font-size: 1rem; }}/* Только для печати */@media print {  .site-header { display: none; }}

Три экрана сразу

Одна страница в размерах телефона, планшета и ноутбука — видно, где ломается.

ЭкранШирина
Телефон360 px
Планшет768 px
Ноутбук1440 px

#Медиазапросы о человеке, а не об экране

Ширина — не единственное, о чём можно спросить. Браузер знает и о настройках человека.

CSSТри важных запроса
/* Человек выбрал тёмную тему в системе */@media (prefers-color-scheme: dark) {  :root {    --bg: #10141f;    --text: #e8eaf2;  }}/* Человеку мешает движение: вестибулярные нарушения, укачивание */@media (prefers-reduced-motion: reduce) {  *, *::before, *::after {    animation-duration: 0.001ms !important;    transition-duration: 0.001ms !important;  }}/* Устройство с грубым указателем — палец, а не мышь */@media (pointer: coarse) {  .btn { min-height: 44px; }}

#Половина медиазапросов не нужна

Современный CSS умеет подстраиваться без всяких запросов. Три приёма закрывают большинство случаев.

CSSГибкость вместо условий
/* 1. Сетка сама считает число колонок */.grid {  display: grid;  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));  gap: 1rem;}/* 2. Размер плавно растёт вместе с окном */h1 {  font-size: clamp(1.8rem, 1.2rem + 3vw, 3.5rem);}/* 3. Контейнер сам себя ограничивает и центрирует */.container {  width: min(100% - 2rem, 1200px);  margin-inline: auto;}

Функции min(), max() и clamp() считают значение прямо во время отрисовки. width: min(100% - 2rem, 1200px) читается так: «сколько поместится, но не больше 1200 пикселей, и всегда с отступом по 1rem с краёв».

Меняй ширину окна браузера и смотри
В песочницу

#Картинки на разных экранах

Про srcset и picture мы говорили в пятом уроке. Но есть правило, без которого не обходится ни один проект.

CSSПишут в каждом проекте
img, video, svg {  max-width: 100%;  height: auto;}

#Container queries: запрос к родителю

У медиазапросов есть слабое место: они знают только ширину окна. А карточка может стоять и в широкой колонке, и в узкой боковой панели на одном и том же экране.

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

CSSКомпонент, который знает своё место
.card-wrap {  container-type: inline-size;}/* Сработает, когда ширина .card-wrap больше 400px,   независимо от размера окна */@container (min-width: 400px) {  .card {    display: grid;    grid-template-columns: 120px 1fr;    gap: 16px;  }}

Где спотыкаются почти все

Забыт мета-viewport

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

Как надо Строка <meta name="viewport" content="width=device-width, initial-scale=1"> в head.

Точки перелома подобраны под конкретные модели телефонов

Почему Устройств сотни, и список устаревает каждый год. Между «подогнанными» размерами вёрстка ломается.

Как надо Ставь границу там, где раскладка начинает выглядеть плохо при плавном изменении ширины.

Ширины блоков заданы в пикселях

Почему Блок шириной 400px на экране 360px вызывает горизонтальную прокрутку всей страницы.

Как надо Бери max-width, проценты, fr и min().

overflow-x: hidden на body «чтобы не скроллилось вбок»

Почему Это прячет симптом. Элемент по-прежнему вылезает, просто теперь его не видно, а вместе с ним ломается position: sticky.

Как надо Найди, что именно выходит за край, и почини причину.

Адаптивность responsive design
Свойство сайта подстраиваться под размер экрана без отдельной мобильной версии.
Медиазапрос media query
Условие, при котором применяются стили: ширина, тема, тип указателя.
Точка перелома breakpoint
Ширина, на которой меняется раскладка.
Mobile-first mobile first
Подход, при котором базовые стили пишутся для узкого экрана, а запросы только добавляют.
Контейнерный запрос container query
Условие по ширине родителя, а не окна.

Проверь себя

  1. Что произойдёт, если забыть мета-viewport?

    Браузер притворится, что экран шириной около 980px, и отмасштабирует всё. Медиазапросы при этом тоже не сработают как надо.

  2. Что означает подход mobile-first?

    Один и тот же сайт, но порядок написания стилей другой: от простого к сложному, через <code>min-width</code>.

  3. Что делает <code>width: min(100% - 2rem, 1200px)</code>?

    Функция <code>min()</code> выбирает меньшее из значений. На узком экране это ширина минус отступы, на широком — 1200px.

  4. Чем container query отличается от media query?

    Благодаря этому компонент подстраивается под место, куда его положили: в широкой колонке он один, в узкой панели — другой.

  5. Какие медиазапросы спрашивают о предпочтениях человека, а не об экране?

    Верных вариантов несколько.

    Ширина — про экран. Тема, чувствительность к движению и тип указателя — про человека и его настройки.

Задание

Сделать раскладку адаптивной

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

  • базовые стили — одна колонка, блоки идут друг под другом (это состояние для телефона);
  • начиная с 700px — двухколоночная схема с меню слева;
  • заголовок задай через clamp(), без медиазапросов;
  • контейнер: width: min(...), чтобы не прилипал к краям;
  • картинкам добавь max-width: 100%.
Открыть в песочнице
    Подсказка 1

    База — просто gap и одна колонка. Ничего указывать не нужно: грид по умолчанию кладёт всё в одну колонку.

    Подсказка 2

    Дальше добавь: @media (min-width: 700px) { .layout { grid-template-columns: 160px 1fr; grid-template-areas: "header header" "nav main" "footer footer"; } }

    Подсказка 3

    Не забудь назначить областям блоки внутри того же медиазапроса.

    Показать решение
    HTMLindex.html
    <div class="layout">  <header class="layout__header"><h1>Кофейня «Полка»</h1></header>  <nav class="layout__nav">Меню</nav>  <main class="layout__main">Содержимое страницы</main>  <footer class="layout__footer">Подвал</footer></div>
    CSSstyle.css
    * { box-sizing: border-box; }img, video, svg {  max-width: 100%;  height: auto;}.layout {  display: grid;  gap: 12px;  width: min(100% - 1.5rem, 900px);  margin-inline: auto;}.layout > * {  padding: 14px;  border-radius: 10px;}.layout__header { background: #6544e0; color: #fff; }.layout__nav    { background: #2b9c74; color: #fff; }.layout__main   { background: #f4f4f8; }.layout__footer { background: #14173a; color: #fff; }h1 {  margin: 0;  font-size: clamp(1.3rem, 1rem + 2.5vw, 2.2rem);  line-height: 1.15;}@media (min-width: 700px) {  .layout {    grid-template-columns: 160px 1fr;    grid-template-areas:      "header header"      "nav    main"      "footer footer";  }  .layout__header { grid-area: header; }  .layout__nav    { grid-area: nav; }  .layout__main   { grid-area: main; }  .layout__footer { grid-area: footer; }}

    Проверь себя