Skip to content

Latest commit

 

History

History
818 lines (609 loc) · 37.8 KB

File metadata and controls

818 lines (609 loc) · 37.8 KB

Урок 12. Адаптивная вёрстка: медиа-запросы, mobile-first, брейкпоинты

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

Все страницы, которые мы строили с Уроков 1 по 11 — включая Flexbox-практику и страницу фильмов на Grid — рассчитаны на десктопный экран. Если открыть их на телефоне, сетка карточек сплющится, aside с фильтрами сожмётся до нечитаемой полоски, а header с логотипом, названием и навигацией в одну строку вообще может вылезти за пределы экрана.

Мы намеренно откладывали эту тему до отдельного урока — в уроках про Flexbox и Grid было сказано «адаптивную вёрстку пока не углубляем». Пришло время закрыть этот пробел.

Адаптивная вёрстка (responsive design) — это подход, при котором один и тот же HTML показывает себя по-разному в зависимости от размера экрана, на котором его открыли. Никакой отдельной «мобильной версии сайта» с другим адресом — один код, разное поведение CSS.

Сегодня мы возьмём страницу фильмов из Урока 11 и сделаем её адаптивной: на телефоне — одна колонка, на планшете — две, на десктопе — как было.


Проблема без медиа-запросов

Откроем практический пример. Вот фрагмент шапки в духе того, что мы делали в Уроке 10:

<header class="header">
  <div class="logo">MovieGrid</div>
  <nav class="nav">
    <a href="#">Главная</a>
    <a href="#">Фильмы</a>
    <a href="#">Контакты</a>
  </nav>
</header>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 32px;
  background: #2d3436;
  color: white;
}

.nav {
  display: flex;
  gap: 24px;
}

На экране 1200px это выглядит отлично — логотип слева, навигация справа. На экране 375px (типичный телефон) слова начинают налезать друг на друга, а если добавить в навигацию ещё пару пунктов — шапка попросту "поедет" горизонтальным скроллом.

Без специального инструмента CSS не умеет менять поведение в зависимости от ширины экрана — свойства применяются одинаково всегда. Этот инструмент — медиа-запросы.


Meta viewport — обязательное условие адаптивности

Прежде чем говорить о медиа-запросах, важно закрепить одну строку, которую вы уже видели в примерах прошлых уроков в <head>, но не разбирали подробно:

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

Без этого тега мобильные браузеры обманывают страницу: они рендерят её так, будто экран имеет ширину ~980px, а затем визуально сжимают результат, чтобы он поместился на маленький дисплей. Из-за этого весь текст становится нечитаемо мелким, а медиа-запросы, ориентированные на реальную ширину экрана, просто не будут срабатывать так, как вы ожидаете.

Разбор атрибутов:

Значение Что делает
width=device-width Ширина области просмотра равна физической ширине экрана устройства
initial-scale=1.0 Начальный масштаб — 1:1, без автоматического уменьшения

Правило: этот тег должен присутствовать в <head> каждой адаптивной страницы. Без него всё, что мы разберём дальше, не будет работать корректно на реальных телефонах.


Синтаксис медиа-запросов

Медиа-запрос (@media) — это условие в CSS: «применяй эти стили, только если выполняется вот такое условие про экран».

<header class="header">
  <div class="logo">MovieGrid</div>

  <nav class="nav">
    <a href="#">Главная</a>
    <a href="#">Фильмы</a>
    <a href="#">Контакты</a>
  </nav>
</header>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;

  padding: 16px 24px;

  background: #2d3436;
  color: white;
}

.logo {
  font-size: 22px;
  font-weight: bold;
}

.nav {
  display: flex;
  gap: 20px;
}

.nav a {
  color: white;
  text-decoration: none;
}

@media (max-width: 768px) {
  .header {
    flex-direction: column;
    align-items: flex-start;
  }

  .nav {
    flex-direction: column;
    gap: 10px;
    margin-top: 12px;
  }
}

Здесь .header станет колонкой (элементы друг под другом), но только если ширина области просмотра 768px или меньше. На больших экранах это правило просто не применится, и сработают обычные стили .header из основного CSS.

Основные условия (media features)

Условие Срабатывает, когда
max-width: 768px ширина экрана ≤ 768px
min-width: 768px ширина экрана ≥ 768px
max-height: 600px высота экрана ≤ 600px
orientation: portrait экран выше, чем шире (телефон вертикально)
orientation: landscape экран шире, чем выше

В 95% практических задач вам понадобятся именно min-width и max-width — они отвечают за то, что реально называют "адаптивной вёрсткой". Остальные условия используются реже, для частных случаев.

Комбинирование условий

Можно указать диапазон — стили применятся, только если ширина экрана попадает между двумя значениями:

@media (min-width: 768px) and (max-width: 1023px) {
  /* только планшеты: от 768px до 1023px включительно */
}

and — это логическое "И": оба условия должны быть истинны одновременно.

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

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

<div class="card">
  <h3>Название фильма</h3>

  <p>
    Краткое описание фильма для демонстрации
    медиа-запросов.
  </p>

  <a href="#">Подробнее</a>
</div>
.card {
  max-width: 320px;

  padding: 16px;

  border-radius: 8px;
  background: white;

  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.card h3 {
  margin-top: 0;
}

.card a {
  display: inline-block;
  margin-top: 10px;
}

@media (max-width: 480px) {
  .card {
    padding: 8px;
  }
}

Важный момент: медиа-запрос не создаёт новый CSS с нуля — он переопределяет уже заданные свойства, если выполняется его условие. Порядок в файле имеет значение (как и в обычном каскаде CSS) — более поздние правила с тем же приоритетом побеждают более ранние.


Mobile-first vs Desktop-first

Здесь принимается стратегическое решение, которое повлияет на весь ваш CSS: с какого экрана начинать писать стили — с маленького или с большого.

Desktop-first (стили "по умолчанию" — для десктопа)

/* Базовые стили — рассчитаны на десктоп */
.container {
  display: flex;
  flex-direction: row;
}

/* Донастройка вниз, для меньших экранов */
@media (max-width: 1024px) {
  .container {
    flex-direction: column;
  }
}

@media (max-width: 480px) {
  .container {
    gap: 8px;
  }
}

Мы пишем полноценную десктопную версию, а затем "откатываем" стили для меньших экранов через max-width.

Mobile-first (стили "по умолчанию" — для мобильного)

/* Базовые стили — рассчитаны на мобильный экран */
.container {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Донастройка вверх, для больших экранов */
@media (min-width: 768px) {
  .container {
    flex-direction: row;
    gap: 16px;
  }
}

Мы пишем базовую версию для самого маленького экрана, а затем "наращиваем" сложность и возможности вёрстки по мере роста экрана через min-width.

Почему индустрия выбрала mobile-first

  1. Простота базового случая. На маленьком экране обычно меньше сложных раскладок — одна колонка, простая структура. Именно с простого удобнее начинать.
  2. Производительность. Мобильные устройства первыми получают минимальный, самый лёгкий набор стилей — без лишней сложной раскладки, которая им не нужна.
  3. Меньше противоречий в каскаде. С min-width вы только добавляете сложность по мере роста экрана. С max-width вы вынуждены постоянно отменять то, что уже написали для десктопа — легче забыть что-то "переопределить назад" и получить баг.
  4. Соответствует реальности трафика. На большинстве современных проектов мобильный трафик равен или превышает десктопный — разумно, чтобы "дефолтная", самая часто используемая версия сайта была лёгкой и рассчитанной именно на мобильный.

Итог: в этом курсе мы будем придерживаться mobile-first — начинаем с простого мобильного макета и добавляем min-width-запросы по мере роста экрана.


Брейкпоинты — где именно ставить границы

Брейкпоинт — это значение ширины экрана, на котором вёрстка меняет своё поведение (например, колонка становится строкой).

Главное правило: брейкпоинты подбираются под контент, а не под конкретные устройства

Частая ошибка новичков — искать "точные размеры экрана iPhone 15" и ставить брейкпоинт именно под это значение. Проблема в том, что моделей устройств тысячи, а их размеры экранов постоянно меняются с выходом новых линеек. Правильный подход — увеличивать ширину окна браузера до тех пор, пока макет не начнёт визуально "ломаться", и ставить брейкпоинт именно в этой точке — независимо от того, какое устройство имеет такую ширину.

Тем не менее, для старта удобно опираться на условные ориентиры, которые покрывают подавляющее большинство реальных экранов:

Диапазон Условное название Типичные устройства
до 480px Маленький мобильный Телефоны в портретной ориентации
480px – 767px Мобильный Телефоны, малые планшеты
768px – 1023px Планшет iPad и аналоги
1024px – 1279px Малый десктоп / ноутбук Ноутбуки, iPad landscape
1280px и больше Десктоп Мониторы

Эти цифры — не строгий стандарт, а ориентир. На практике в большинстве проектов достаточно 2–3 брейкпоинтов, а не всех пяти сразу — избыточное дробление усложняет поддержку CSS без реальной пользы.

Пример: три брейкпоинта для карточной сетки

<div class="cards">
  <article class="card">Фильм 1</article>
  <article class="card">Фильм 2</article>
  <article class="card">Фильм 3</article>
  <article class="card">Фильм 4</article>
  <article class="card">Фильм 5</article>
  <article class="card">Фильм 6</article>
</div>
/* Mobile-first: базово — 1 колонка */
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.card {
  padding: 32px;
  background: steelblue;
  color: white;
  text-align: center;
  border-radius: 8px;
}

/* Планшет: 2 колонки */
@media (min-width: 768px) {
  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Десктоп: 3 колонки */
@media (min-width: 1024px) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

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


Относительные единицы — фундамент адаптивности

Мы разбирали единицы измерения в Уроке 8, но именно в контексте адаптивной вёрстки становится понятно, зачем они вообще нужны. Если вы верстаете фиксированными px значениями везде — адаптивность придётся строить целиком на медиа-запросах, переопределяя буквально каждое значение. Если вы используете относительные единицы с самого начала — многое подстроится "само", без единого @media.

Быстрое напоминание

Единица От чего зависит Где уместна
px Ничего, абсолютное значение Border, тонкие декоративные детали
% Размер родителя Ширина колонок внутри контейнера
rem Размер шрифта корневого элемента (html) Отступы, размеры шрифтов — масштабируются вместе с базовым размером
em Размер шрифта текущего элемента Внутренние отступы относительно текста конкретного блока
vw / vh Ширина / высота окна просмотра (1vw = 1% ширины экрана) Полноэкранные блоки, "резиновая" типографика

clamp() — плавное масштабирование без медиа-запросов

clamp(min, preferred, max) — функция CSS, которая выбирает значение в заданном диапазоне: минимум, "желаемое" значение (обычно во vw), максимум. Она особенно полезна для типографики и отступов, потому что позволяет избежать медиа-запроса вообще там, где нужно плавное изменение, а не резкий скачок.

<section class="section">
  <h1>MovieGrid</h1>

  <p>
    Размер заголовка и внутренние отступы этого блока
    плавно изменяются вместе с шириной окна браузера.
  </p>
</section>
.section {
  padding: clamp(16px, 4vw, 64px);

  background: #f4f4f4;
  border-radius: 8px;
}

h1 {
  margin-top: 0;

  /* не меньше 24px, не больше 48px,
     а между ними — 5% ширины экрана */
  font-size: clamp(24px, 5vw, 48px);
}

На маленьком экране font-size будет 24px (упёрлось в минимум), на среднем — где-то между, плавно растёт вместе с шириной экрана, на большом — упрётся в 48px и дальше расти не будет.

Важно: clamp() не заменяет медиа-запросы полностью — он прекрасно решает задачу плавного масштабирования одного значения (шрифт, отступ, ширина блока), но не решает задачу структурной перестройки макета (например, превращения ряда карточек в колонку). Для структурных изменений медиа-запросы всё равно нужны.


Практика: делаем адаптивной шапку сайта

Вернёмся к шапке из начала урока и сделаем её mobile-first.

<header class="header">
  <div class="logo">MovieGrid</div>

  <nav class="nav">
    <a href="#">Главная</a>
    <a href="#">Фильмы</a>
    <a href="#">Сериалы</a>
    <a href="#">Контакты</a>
  </nav>
</header>
/* Mobile-first: базовые стили рассчитаны на маленький экран */
.header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;

  padding: 16px 20px;

  background: #2d3436;
  color: white;
}

.logo {
  font-size: 20px;
  font-weight: bold;
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 8px;

  width: 100%;
}

.nav a {
  color: white;
  text-decoration: none;

  padding: 8px 0;

  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

/* Планшет и больше: шапка становится строкой */
@media (min-width: 768px) {
  .header {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;

    padding: 16px 32px;
  }

  .nav {
    flex-direction: row;
    gap: 24px;

    width: auto;
  }

  .nav a {
    padding: 0;
    border-bottom: none;
  }
}

Разбор: на телефоне логотип и пункты меню идут друг под другом на всю ширину экрана — с ними удобно взаимодействовать пальцем. На экране от 768px и шире шапка "разворачивается" в привычную горизонтальную строку — flex-direction: row возвращает поведение, знакомое по Уроку 10.

Попробуйте изменять ширину окна браузера. До 768px меню располагается столбцом, а после достижения этого значения автоматически перестраивается в горизонтальную строку.


Практика: адаптируем всю страницу фильмов (Урок 11)

Теперь применим тот же принцип к целой странице — той самой, что мы верстали в конце Урока 11 на grid-template-areas.

<header>
  <h1>MovieGrid</h1>
</header>

<aside>
  <h2>Фильтры</h2>

  <ul>
    <li>Боевики</li>
    <li>Комедии</li>
    <li>Фантастика</li>
    <li>Драмы</li>
  </ul>
</aside>

<main>
  <div class="cards">
    <article class="card">Фильм 1</article>
    <article class="card">Фильм 2</article>
    <article class="card">Фильм 3</article>
    <article class="card">Фильм 4</article>
    <article class="card">Фильм 5</article>
    <article class="card">Фильм 6</article>
  </div>
</main>

<footer>
  © MovieGrid
</footer>

Напомним десктопную структуру:

body {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: 80px 1fr 60px;
  grid-template-areas:
    "header header"
    "aside  main"
    "footer footer";
}

На телефоне колонка в 250px под aside попросту не помещается рядом с контентом — а фильтры сбоку на маленьком экране вообще не самый удобный паттерн. Переверстаем по mobile-first: сначала — всё в один поток, друг под другом, потом при росте экрана — возвращаем двухколоночную структуру.

/* Mobile-first: все блоки идут один под другим */
body {
  display: flex;
  flex-direction: column;

  min-height: 100vh;
  margin: 0;

  font-family: sans-serif;
}

header {
  background: #6c5ce7;
  color: white;
  padding: 16px 20px;
}

aside {
  background: #a29bfe;
  color: white;
  padding: 16px 20px;
}

aside ul {
  margin: 12px 0 0;
  padding-left: 20px;
}

main {
  flex: 1;
  padding: 16px 20px;
}

footer {
  background: #636e72;
  color: white;
  padding: 16px 20px;
}

/* Карточки фильмов */
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.card {
  padding: 32px;

  background: #f5f6fa;
  border-radius: 8px;

  text-align: center;
}

/* Планшет */
@media (min-width: 768px) {
  body {
    display: grid;

    grid-template-columns: 200px 1fr;
    grid-template-rows: 80px 1fr 60px;

    grid-template-areas:
      "header header"
      "sidebar main"
      "footer footer";
  }

  header {
    grid-area: header;
  }

  aside {
    grid-area: sidebar;
  }

  main {
    grid-area: main;
  }

  footer {
    grid-area: footer;
  }

  .cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Десктоп */
@media (min-width: 1024px) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

Ключевая идея этого примера: мы не просто "сжимаем" одну и ту же сетку — мы меняем сам тип раскладки. На мобильном body — это flex с колонкой из блоков друг под другом (Grid с areas тут не нужен, для одной колонки он избыточен). На планшете и десктопе body уже полноценный grid с areas, как в оригинале Урока 11. Медиа-запрос может переопределять не только частные свойства, но и саму стратегию раскладки контейнера.


Подводные камни

Забытый viewport meta-тег

Без <meta name="viewport" ...> все медиа-запросы, ориентированные на реальную ширину экрана, будут срабатывать некорректно на настоящих телефонах — хотя в инструментах разработчика в браузере всё может выглядеть нормально. Проверяйте этот тег в первую очередь, если адаптивность "не работает на телефоне, но работает в браузере".

Смешивание mobile-first и desktop-first в одном проекте

/* Плохо: одни блоки используют min-width, другие — max-width */
@media (min-width: 768px) { .header { flex-direction: row; } }
@media (max-width: 767px) { .cards { grid-template-columns: 1fr; } }

Технически это может сработать, но такой код сложно читать и поддерживать — приходится держать в голове две разные логики одновременно. Выберите одну стратегию (в этом курсе — mobile-first) и придерживайтесь её во всём проекте.

Слишком много брейкпоинтов "про запас"

/* Излишне: пять брейкпоинтов для простой сетки карточек */
@media (min-width: 400px) { ... }
@media (min-width: 600px) { ... }
@media (min-width: 800px) { ... }
@media (min-width: 1000px) { ... }
@media (min-width: 1200px) { ... }

Каждый новый брейкпоинт — это дополнительная точка, которую нужно тестировать и поддерживать. Начинайте с 2–3 брейкпоинтов и добавляйте новые только тогда, когда реально увидели, что макет "ломается" на промежуточной ширине.

Фиксированные px-ширины внутри медиа-запроса

/* Плохо: на любом экране от 768px карточка будет ровно 700px шириной */
@media (min-width: 768px) {
  .card {
    width: 700px;
  }
}

На экране 768px такая карточка вылезет за пределы экрана. Внутри медиа-запросов, как и вне них, предпочтительны относительные единицы (%, fr в Grid, flex: 1) — тогда блок будет корректно себя вести на любой ширине внутри диапазона, а не только на той единственной ширине, на которой вы тестировали.

Тестирование только через изменение размера окна браузера

Изменение ширины окна десктопного браузера — это не то же самое, что реальный телефон: разные пропорции экрана, разная плотность пикселей, разное поведение шрифтов и скроллбаров. Обязательно проверяйте вёрстку через "Инструменты разработчика" → режим эмуляции устройств (обычно иконка телефона/планшета в панели инструментов) — там можно выбрать конкретные модели устройств с реальными размерами экрана.


Итоги урока

Адаптивная вёрстка строится на @media-запросах — блоках CSS-правил, которые применяются только при выполнении условия про ширину (реже — высоту или ориентацию) экрана. Обязательное условие корректной работы медиа-запросов на реальных устройствах — тег <meta name="viewport" content="width=device-width, initial-scale=1.0"> в <head>.

Мы выбрали стратегию mobile-first: базовые стили рассчитаны на маленький экран, а min-width-запросы постепенно добавляют сложность по мере роста экрана. Это противоположность desktop-first, где max-width-запросы, наоборот, "откатывают" уже написанные десктопные стили вниз.

Брейкпоинты — это не привязка к конкретным моделям устройств, а точки, где реальная вёрстка начинает визуально ломаться. На старте достаточно 2–3 брейкпоинтов (условно: мобильный / планшет / десктоп).

Относительные единицы (%, rem, vw) и функция clamp() снижают количество медиа-запросов, которые вообще придётся писать — многое может "подстроиться" плавно, без явных условий. Но структурные изменения раскладки (превращение колонки в строку, смена flex на grid) всё равно требуют явных медиа-запросов.

В следующем уроке разберём стилизацию input — не менее важную часть подготовки к формам, которыми мы закроем весь HTML/CSS-блок курса.


Вопросы для проверки

  1. Почему без тега <meta name="viewport"> медиа-запросы работают некорректно на реальных мобильных устройствах?
  2. В чём разница между mobile-first и desktop-first подходами? Какой из них используется в этом курсе и почему?
  3. Как правильно выбирать значения для брейкпоинтов — по каким критериям?
  4. Что делает функция clamp() и в каких случаях она не заменяет медиа-запрос?
  5. В примере с адаптацией страницы фильмов (Урок 11) на мобильном экране body использует flex, а на планшете и десктопе — grid с grid-template-areas. Почему нельзя было просто оставить grid на всех экранах и просто менять grid-template-columns?
  6. Чем плохо использование фиксированных px-значений для ширины блоков внутри медиа-запроса?

Практические задания

Задание 1. Адаптивная шапка (расширение практики Урока 10)

Возьмите шапку сайта, которую вы создавали в самостоятельной практике Урока 10 (логотип, название, навигация). Сделайте её адаптивной по принципу mobile-first:

  1. На экране уже с ширины 0 (мобильный, база) — логотип и пункты навигации расположены вертикально, друг под другом, с отступами между ними.
  2. При достижении ширины 768px — шапка превращается в горизонтальную строку: логотип слева, навигация справа, вертикально выровнены по центру.
  3. Добавьте <meta name="viewport"> в <head>, если его там ещё нет.
  4. Проверьте результат через инструменты разработчика в режиме эмуляции устройств, на 2–3 разных ширинах экрана.
Готовая html разметка
<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Адаптивная шапка</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <header class="header">
    <div class="logo">MyLogo</div>
    <nav class="nav">
      <a href="#">Главная</a>
      <a href="#">О нас</a>
      <a href="#">Услуги</a>
      <a href="#">Контакты</a>
    </nav>
  </header>
</body>
</html>

Задание 2. Адаптивная сетка карточек (расширение практики Урока 11)

Возьмите блок с карточками фильмов из практики Урока 11 (6 карточек: изображение, название, описание, кнопка). Сделайте сетку карточек адаптивной:

  1. Mobile-first: на маленьком экране — 1 колонка.
  2. На ширине от 600px — 2 колонки.
  3. На ширине от 1024px — 3 колонки.
  4. Отступы между карточками (gap) на мобильном должны быть меньше, чем на десктопе — например, 12px против 24px.
Готовая html разметка

HTML (фрагмент, повторяется 6 раз):

<div class="cards">
  <article class="card">
    <img class="card__cover" src="img/movie1.jpg" alt="Название фильма" />
    <h3 class="card__title">Название фильма</h3>
    <p class="card__desc">Краткое описание фильма для примера карточки.</p>
    <a class="card__link" href="#">Смотреть</a>
  </article>
  <!-- ...ещё 5 таких же карточек... -->
</div>

Задание 3. Полностью адаптивная страница фильмов (объединяющая практика)

Возьмите целиком страницу фильмов из финальной практики Урока 11 (header + aside + main с карточками + footer на grid-template-areas) и объедините решения заданий 1 и 2 в единый адаптивный документ:

  1. Ниже 768px: body — простой вертикальный поток (flex-direction: column), никакого grid-template-areas. Шапка — как в Задании 1. Карточки — 1 колонка, как в Задании 2. aside с фильтрами располагается между header и main, на всю ширину.
  2. От 768px до 1023px: body переключается на grid с grid-template-areas (header header / aside main / footer footer), aside — 200px, карточки — 2 колонки.
  3. От 1024px: карточки — 3 колонки, остальная структура как на планшете.
  4. Убедитесь, что переходы между всеми тремя состояниями визуально плавные — ничего не "прыгает" и не съезжает при изменении ширины окна.
Готовая html разметка

Вместо <article class="card">Фильм 1</article> подставить карточку из задания 2.

<header>
  <h1>MovieGrid</h1>
</header>

<aside>
  <h2>Фильтры</h2>

  <ul>
    <li>Боевики</li>
    <li>Комедии</li>
    <li>Фантастика</li>
    <li>Драмы</li>
  </ul>
</aside>

<main>
  <div class="cards">
    <article class="card">Фильм 1</article>
    <article class="card">Фильм 2</article>
    <article class="card">Фильм 3</article>
    <article class="card">Фильм 4</article>
    <article class="card">Фильм 5</article>
    <article class="card">Фильм 6</article>
  </div>
</main>

<footer>
  © MovieGrid
</footer>

Предыдущий урок | Следующий урок