К списку уроков

Урок 3. Формы и введение в CSS: селекторы, каскад, блочная модель, Flexbox и Grid

Онлайн и в Б-3, 5 октября (понедельник), 15:00

Предисловие

Для вашего удобства текстовый курс перенесен на отдельную платформу. Ссылка для поступления: тык

Мы настоятельно рекомендуем пользоваться именно этой платформой по ходу курса. Ссылка на текущее занятие: тык

⚠️Прежде чем переходить к заданиям - обязательно зарегистрируйтесь на курс. Иначе вас закинет на общий поток и вы не сможете взаимодействовать с сверстниками во втором модуле курса. Это не смертельно и вполне решаемо, но тем не менее.

Формы и введение в CSS: селекторы, каскад, блочная модель, Flexbox и Grid

Формы

Форма собирает данные, которые вводит пользователь: имя, адрес почты, выбранный вариант, отметку о согласии. В HTML форму описывает тег <form>, а внутри него находятся поля и кнопка отправки.

<form action="/subscribe" method="get">
  <label for="email">Почта для новостей магазина</label>
  <input id="email" name="email" type="email" />
  <button type="submit">Подписаться</button>
</form>

Введите что-нибудь в поле и нажмите «Подписаться». В предпросмотре форма не уходит на сервер: под примером появится строка с данными, которые браузер отправил бы.

  • Атрибут action у <form> задаёт адрес, на который браузер отправит данные.

  • Атрибут method задаёт способ отправки. При get данные попадают в адрес страницы: /subscribe?email=anna@example.com. При post данные передаются в теле запроса и в адресе не видны. Формы с личными данными и паролями отправляют методом post.

  • Атрибут name у поля задаёт имя, под которым значение поля уйдёт на сервер. Поле без name браузер не отправляет.

  • Кнопка с type="submit" отправляет форму. Форму также отправляет нажатие Enter в текстовом поле.

Позже мы будем перехватывать отправку формы в JavaScript и отправлять данные сами. Но разметку формы всё равно пишут через <form>: благодаря ей работают отправка по Enter, автозаполнение и встроенная проверка полей.

Почитать ещё

Поле ввода input

Тег <input> создаёт однострочное поле. Это одиночный тег, и его поведение зависит от атрибута type.

<p><input type="text" placeholder="text: обычная строка" /></p>
<p><input type="email" placeholder="email: адрес почты" /></p>
<p><input type="password" placeholder="password: пароль" /></p>
<p><input type="number" min="1" max="10" value="1" /> number: число</p>
<p><input type="date" /> date: дата</p>
<p><input type="search" placeholder="search: поиск" /></p>
<p><input type="range" min="0" max="100" /> range: ползунок</p>

type

Что вводит пользователь

text

Любую строку. Этот тип используется, если type не указан.

email, url, tel

Адрес почты, ссылку, телефон. Браузер проверяет формат почты и ссылки, а на телефоне показывает подходящую клавиатуру.

password

Пароль. Вместо символов браузер показывает точки.

number

Число. Атрибуты min, max и step ограничивают значение и шаг.

range

Число в диапазоне с помощью ползунка.

date, time, datetime-local

Дату, время или дату со временем через встроенный календарь.

checkbox, radio

Отметку или выбор варианта. Их разберём в следующем разделе.

file

Файл с компьютера.

search

Поисковый запрос. Некоторые браузеры добавляют в такое поле кнопку очистки.

У <input> есть общие атрибуты:

  • value задаёт начальное значение поля;

  • placeholder показывает подсказку внутри пустого поля;

  • disabled отключает поле: его нельзя изменить, и форма его не отправляет;

  • readonly запрещает изменять значение, но форма его отправляет.

Любое значение поля браузер хранит как строку, даже если у поля type="number". Об этом придётся помнить, когда мы начнём считать значения полей в JavaScript.

Почитать ещё

Флажки, переключатели, списки и многострочный текст

Кроме текстовых полей, в формах используют ещё несколько элементов.

<p>
  <input id="gift" name="gift" type="checkbox" />
  <label for="gift">Упаковать в подарочную коробку</label>
</p>

<p>Способ доставки:</p>
<p>
  <input id="courier" name="delivery" type="radio" value="courier" checked />
  <label for="courier">Курьером</label>
  <input id="pickup" name="delivery" type="radio" value="pickup" />
  <label for="pickup">Самовывоз</label>
</p>

<p>
  <label for="city">Город</label>
  <select id="city" name="city">
    <option value="msk">Москва</option>
    <option value="spb" selected>Санкт-Петербург</option>
    <option value="kzn">Казань</option>
  </select>
</p>

<p>
  <label for="comment">Комментарий к заказу</label><br />
  <textarea id="comment" name="comment" rows="3" cols="40"></textarea>
</p>
  • <input type="checkbox"> — флажок, который включают и выключают. Отмеченный флажок отправляет своё значение, неотмеченный не отправляется совсем. Атрибут checked отмечает флажок заранее.

  • <input type="radio"> — переключатель. Переключатели с одинаковым name образуют группу, и в группе можно выбрать только один вариант. На сервер уходит value выбранного переключателя.

  • <select> — выпадающий список. Варианты описывают тегами <option>, на сервер уходит value выбранного варианта. Атрибут selected выбирает вариант заранее.

  • <textarea> — поле для многострочного текста. В отличие от <input>, это парный тег, а начальный текст пишут между открывающим и закрывающим тегами. Атрибут rows задаёт высоту поля в строках.

Связанные поля объединяют тегом <fieldset>, а его заголовок записывают в <legend>. Например, так группируют поля адреса доставки или переключатели способа оплаты.

Почитать ещё

  • Дока: <select>, <textarea>, <fieldset>

Подписи к полям

У каждого поля должна быть подпись, из которой понятно, что в него вводить. Подпись описывает тег <label>.

Связать подпись с полем можно двумя способами:

<p>
  <label for="name">Имя</label>
  <input id="name" name="name" type="text" />
</p>

<p>
  <label>
    <input name="agree" type="checkbox" />
    Согласен на обработку персональных данных
  </label>
</p>
  • В первом способе атрибут for у <label> совпадает с id поля.

  • Во втором способе поле находится внутри <label>.

Нажмите на слово «Имя» в предпросмотре: курсор перейдёт в поле. Нажмите на текст о согласии: флажок включится. Так работает только подпись, связанная с полем.

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

Атрибут placeholder не заменяет подпись. Подсказка исчезает, как только пользователь начинает печатать, и при заполнении длинной формы легко забыть, какое поле какое. Кроме того, текст подсказки обычно бледный и плохо читается.

Почитать ещё

  • Дока: <label>

Встроенная проверка полей

Браузер умеет проверять значения полей до отправки формы. Правила проверки задают атрибутами.

Атрибут

Правило

required

Поле обязательно для заполнения

minlength, maxlength

Минимальная и максимальная длина текста

min, max, step

Границы и шаг числа или даты

type="email", type="url"

Значение должно быть похоже на адрес почты или ссылку

pattern

Значение должно подходить под регулярное выражение, например pattern="[0-9]{6}" для индекса из шести цифр

<form>
  <p>
    <label for="order-name">Имя</label>
    <input id="order-name" name="name" type="text" required minlength="2" />
  </p>
  <p>
    <label for="order-email">Почта</label>
    <input id="order-email" name="email" type="email" required />
  </p>
  <p>
    <label for="order-index">Индекс</label>
    <input id="order-index" name="index" type="text" pattern="[0-9]{6}" />
  </p>
  <button type="submit">Оформить заказ</button>
</form>

Нажмите «Оформить заказ» с пустыми полями, затем введите почту без символа @ или индекс из пяти цифр. Браузер не отправит форму и покажет подсказку возле первого поля с ошибкой.

Поля, которые прошли или не прошли проверку, можно оформить в CSS с помощью псевдоклассов :valid и :invalid. Атрибут novalidate у <form> отключает встроенную проверку. Так делают, когда проверку и сообщения об ошибках пишут на JavaScript, и мы так поступим на тринадцатом занятии.

Встроенная проверка работает только в браузере. Пользователь может отключить её через инструменты разработчика или отправить запрос в обход страницы, поэтому сервер всегда проверяет данные ещё раз.

Почитать ещё

Как подключить CSS

CSS описывает внешний вид страницы: цвета, шрифты, отступы, расположение элементов. Подключить стили к HTML-документу можно тремя способами.

<head>
  <!-- 1. Внешний файл -->
  <link rel="stylesheet" href="style.css" />

  <!-- 2. Тег style внутри документа -->
  <style>
    h1 {
      color: #4f46e5;
    }
  </style>
</head>
<body>
  <!-- 3. Атрибут style у элемента -->
  <p style="color: red;">Акция заканчивается сегодня</p>
</body>

Внешний файл — основной способ. Один файл стилей подключают ко всем страницам сайта, и оформление остаётся одинаковым. Браузер сохраняет скачанный файл в кэше и при переходе на другую страницу не скачивает его заново.

Тег <style> удобен для небольших экспериментов и примеров, но стили из него действуют только на одну страницу.

Атрибут style задаёт стили одному элементу. Такие стили сложно переиспользовать и переопределить, поэтому вручную их пишут редко. Зато JavaScript часто меняет стили элемента именно через этот атрибут.

Путь в href указывают относительно HTML-файла. Если style.css лежит в той же папке, пишут href="style.css". Если в папке css, пишут href="css/style.css".

Если стили не применились, откройте вкладку Network и проверьте запрос файла стилей. Статус 404 означает, что браузер не нашёл файл по указанному пути: чаще всего в пути опечатка.

Почитать ещё

  • Дока: <link>, <style>

Из чего состоит CSS-правило

Файл стилей состоит из правил. Каждое правило отвечает на два вопроса: к каким элементам применить стили и какие это стили.

h1 {
  color: #4f46e5;
  font-size: 32px;
}

.price {
  font-weight: 700;
}
  • h1 и .price — селекторы. Селектор определяет, к каким элементам применяется правило. Селектор h1 выбирает все заголовки первого уровня, а .price — все элементы с классом price.

  • Внутри фигурных скобок находятся объявления. Каждое объявление состоит из свойства и значения, разделённых двоеточием, и заканчивается точкой с запятой.

  • color, font-size и font-weight — свойства: цвет текста, размер шрифта и насыщенность шрифта.

Предпросмотр этого примера показывает страницу с заголовком, описанием и ценой. Её разметка скрыта, чтобы в примере остались только стили.

Если в объявлении ошибка, например свойство написано с опечаткой, браузер пропустит только это объявление и применит остальные. Сообщения об ошибке в консоли не будет. Найти такую опечатку помогает панель Styles в инструментах разработчика: неизвестное свойство там отмечено предупреждающим значком.

Комментарии в CSS записывают как /* текст */.

Почитать ещё

Селекторы

Селектор определяет, к каким элементам применяются стили. Основные виды селекторов:

Селектор

Что выбирает

p

Все элементы <p>

.card

Все элементы с классом card

#catalog

Элемент с id="catalog"

.card p

Элементы <p>, которые находятся внутри .card на любой глубине

.card > p

Элементы <p>, которые являются прямыми детьми .card

h1, h2

Все <h1> и все <h2>

a:hover

Ссылку, на которую наведён курсор

input:focus

Поле, в котором сейчас находится курсор ввода

li:first-child

Элемент <li>, который стоит первым среди своих соседей

input[type="checkbox"]

Элементы <input> с атрибутом type="checkbox"

.card p {
  color: #57606a;
}

.card > .card__price {
  color: #4f46e5;
  font-weight: 700;
}

.card__button:hover {
  background: #4f46e5;
  color: white;
}

Наведите курсор на кнопку в предпросмотре, чтобы сработал псевдокласс :hover.

В проектах стили почти всегда задают через классы. Селектор по классу можно применить к любому количеству элементов, а его имя описывает смысл: .card, .price, .button. Селекторы по тегам используют для общих стилей, например для body или всех ссылок. Селекторы по id для стилей не используют: id уникален на странице, а стили из-за него трудно переопределить. Почему так происходит, разберём в следующем разделе.

Почитать ещё

Каскад и специфичность

К одному элементу часто подходят сразу несколько правил. Если правила задают разные свойства, браузер применит все. Если правила задают одно и то же свойство с разными значениями, браузер выбирает одно значение по правилам каскада.

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

  1. Атрибут style у элемента сильнее любого селектора.

  2. Селектор по id сильнее селектора по классу.

  3. Селектор по классу, атрибуту или псевдоклассу сильнее селектора по тегу.

  4. Селектор по тегу слабее всех перечисленных.

Если в селекторе несколько частей, их вклад складывается: .card p содержит класс и тег, поэтому он сильнее, чем p.

p {
  color: #1f2328;
}

.card p {
  color: #57606a;
}

.note {
  color: #cf222e;
}

.note {
  color: #1a7f37;
}

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

Значение с пометкой !important, например color: red !important, перебивает обычные значения независимо от специфичности. Пометку используют только в крайних случаях: чтобы переопределить такое значение, придётся снова писать !important, и стили быстро становятся запутанными.

Посмотреть, какое правило победило, можно в панели Styles инструментов разработчика. Перебитые объявления там показаны зачёркнутыми.

Почитать ещё

Наследование

Часть свойств CSS наследуется: если задать значение родителю, его получат все вложенные элементы, у которых это свойство не задано отдельно.

body {
  font-family: Georgia, serif;
  color: #4f46e5;
}

.card {
  border: 2px solid #4f46e5;
  padding: 12px;
}

Наследуются свойства, которые относятся к тексту: color, font-family, font-size, font-weight, line-height, text-align. Поэтому шрифт и цвет текста для всей страницы обычно задают один раз в правиле для body.

Не наследуются свойства, которые относятся к самому блоку: отступы margin и padding, рамка border, фон background, размеры width и height. Если бы рамка наследовалась, у карточки из примера рамку получил бы каждый абзац.

Некоторые элементы не наследуют шрифт по умолчанию. Например, у <button>, <input> и <textarea> браузер задаёт свой шрифт. Чтобы поля формы использовали шрифт страницы, им задают font: inherit. Значение inherit заставляет свойство взять значение родителя.

Почитать ещё

Блочная модель

Браузер представляет каждый элемент как прямоугольник. Этот прямоугольник состоит из четырёх областей:

  • content — содержимое: текст, картинка, дочерние элементы;

  • padding — внутренний отступ между содержимым и рамкой;

  • border — рамка;

  • margin — внешний отступ между рамкой элемента и соседними элементами.

.box {
  width: 240px;
  padding: 20px;
  border: 4px solid #4f46e5;
  margin: 16px 0;
  background: #eef2ff;
}

.box--border-box {
  box-sizing: border-box;
}

По умолчанию width и height задают размер только области содержимого. Отступы и рамка добавляются сверху: первый блок в примере занимает 240 + 20 × 2 + 4 × 2 = 288 пикселей в ширину.

Изображение

Свойство box-sizing: border-box меняет этот расчёт: width задаёт ширину вместе с отступами и рамкой. Второй блок занимает ровно 240 пикселей, а на содержимое остаётся 192. Считать размеры так гораздо удобнее, поэтому в начале почти любого файла стилей пишут правило для всех элементов:

*,
*::before,
*::after {
  box-sizing: border-box;
}

У margin и padding есть сокращённая запись. Одно значение margin: 16px задаёт отступ со всех сторон. Два значения margin: 16px 0 задают отступ сверху и снизу, а затем слева и справа. Четыре значения идут по часовой стрелке: сверху, справа, снизу, слева. Значение margin: 0 auto выравнивает блок с заданной шириной по центру родителя.

Размеры всех четырёх областей выбранного элемента показывает схема внизу вкладки Computed в инструментах разработчика.

Почитать ещё

Текст, цвет и фон

Эти свойства встречаются почти в каждом файле стилей.

body {
  font-family: system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  color: #1f2328;
  background: #f4f5f7;
}

.card {
  padding: 16px;
  border-radius: 12px;
  background: white;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.15);
}

.card__title {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 600;
}

.card__price {
  color: #4f46e5;
  font-weight: 700;
  text-transform: uppercase;
}

.card__link {
  color: #4f46e5;
  text-decoration: none;
}
  • font-family задаёт шрифт. Через запятую перечисляют запасные варианты: браузер возьмёт первый доступный. Значение system-ui означает системный шрифт операционной системы, его не нужно скачивать.

  • font-size задаёт размер шрифта, font-weight — насыщенность: 400 соответствует обычному тексту, 700 — жирному.

  • line-height задаёт высоту строки. Значение без единиц 1.5 означает полторы высоты шрифта.

  • color задаёт цвет текста, background — фон элемента.

  • text-align выравнивает текст внутри блока, text-transform: uppercase переводит текст в заглавные буквы, text-decoration: none убирает подчёркивание у ссылок.

  • border-radius скругляет углы, box-shadow добавляет тень.

Цвет записывают несколькими способами: названием (white), шестнадцатеричным кодом (#4f46e5) или функцией rgb(79 70 229). Функция rgb принимает ещё и прозрачность после косой черты: rgb(0 0 0 / 0.15) — чёрный цвет с непрозрачностью 15%.

Почитать ещё

  • Дока: font-family, line-height, color, background, box-shadow

Единицы измерения

Размеры в CSS записывают числом и единицей измерения. Между числом и единицей пробела нет: 16px, а не 16 px. Для нуля единицу можно не писать: margin: 0.

Единица

От чего зависит

px

Пиксели. Размер не зависит от других элементов.

%

Процент от размера родителя. width: 50% — половина ширины родителя.

rem

Размер шрифта корневого элемента <html>. По умолчанию он равен 16 пикселям, поэтому 1.5rem — это 24 пикселя.

em

Размер шрифта текущего элемента.

vw, vh

Процент от ширины и высоты окна браузера. 100vh — вся высота окна.

.bar {
  margin-bottom: 8px;
  padding: 4px 8px;
  background: #eef2ff;
}

.bar--px {
  width: 200px;
}

.bar--percent {
  width: 50%;
}

.bar--rem {
  width: 20rem;
  font-size: 1.25rem;
}

Пиксели понятны и предсказуемы, поэтому начинать удобно с них. Единицу rem часто используют для размеров шрифта: если пользователь увеличит размер шрифта в настройках браузера, текст на странице тоже увеличится. Проценты и vw подходят для размеров, которые должны меняться вместе с шириной экрана.

Почитать ещё

Свойство display

Свойство display определяет, как элемент располагается на странице. На втором занятии мы разделили элементы на блочные и строчные. Это деление задаёт именно display, и его можно изменить.

Значение

Как ведёт себя элемент

block

Начинается с новой строки и занимает всю ширину родителя. Ширину, высоту и отступы можно задать.

inline

Находится внутри строки текста. Свойства width, height и вертикальные margin на него не действуют.

inline-block

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

none

Элемент не отображается и не занимает места.

flex, grid

Элемент становится контейнером, который раскладывает дочерние элементы. Разберём в следующих разделах.

.button {
  display: inline-block;
  padding: 8px 16px;
  border-radius: 8px;
  background: #4f46e5;
  color: white;
  text-decoration: none;
}

.menu li {
  display: inline;
  margin-right: 12px;
}

.hidden {
  display: none;
}

Значение inline-block часто используют, чтобы оформить ссылку как кнопку: ссылка остаётся в строке текста, но получает внутренние отступы. Значение none применяют, чтобы скрыть элемент, например закрытое меню или модальное окно.

Почитать ещё

  • Дока: display

Инструменты разработчика: DOM и стили

На втором занятии мы смотрели на DOM во вкладке Elements. Рядом с деревом элементов находятся панели, которые показывают стили выбранного элемента.

Чтобы выбрать элемент, нажмите на значок стрелки в левом верхнем углу инструментов разработчика или сочетание Ctrl+Shift+C (на Mac — Cmd+Shift+C), а затем щёлкните по нужному месту страницы. Элемент выделится в дереве.

Панель Styles показывает все CSS-правила, которые подходят выбранному элементу, в порядке убывания приоритета. Рядом с каждым правилом указан файл и номер строки. Перебитые объявления зачёркнуты. В панели можно:

  • изменить значение, щёлкнув по нему;

  • добавить новое объявление, щёлкнув по пустому месту внутри правила;

  • временно выключить объявление, сняв флажок рядом с ним.

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

Изменения в панели Styles действуют только до перезагрузки страницы. Удобно подобрать отступ или цвет прямо в браузере, а потом перенести найденное значение в файл стилей.

Когда стиль не применяется, выберите элемент и посмотрите в панель Styles:

  • правила нет в списке — селектор не подходит к элементу, например в имени класса опечатка;

  • правило есть, но объявление зачёркнуто — его перебило другое правило с большей специфичностью или записанное позже;

  • объявление отмечено предупреждающим значком — в имени свойства или в значении ошибка.

Почитать ещё

Flexbox

Flexbox раскладывает элементы в одну линию: в строку или в столбец. С его помощью располагают логотип и меню в шапке, кнопки в ряд, иконку рядом с текстом.

Flexbox включают свойством display: flex у родителя. Такой родитель называется flex-контейнером, а его прямые дети — flex-элементами. Дети сразу выстраиваются в строку.

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  background: #1f2328;
}

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

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

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

У flex-контейнера есть главная ось, вдоль которой выстраиваются элементы, и поперечная ось, перпендикулярная главной. По умолчанию главная ось горизонтальная.

Свойство контейнера

Что делает

flex-direction

Направление главной оси: row — строка, column — столбец

justify-content

Распределяет элементы вдоль главной оси: flex-start, center, flex-end, space-between

align-items

Выравнивает элементы по поперечной оси: stretch, flex-start, center, flex-end

gap

Расстояние между элементами

flex-wrap

Значение wrap переносит элементы на новую строку, если они не помещаются

В примере justify-content: space-between прижимает логотип к левому краю, а меню к правому. align-items: center выравнивает их по вертикали, хотя шрифт логотипа крупнее.

У flex-элементов тоже есть свойство. flex: 1 означает «займи всё свободное место вдоль главной оси». Если свойство flex: 1 есть у двух соседних элементов, они поделят свободное место поровну.

Почитать ещё

Grid

Grid раскладывает элементы по сетке из строк и столбцов. С его помощью строят каталоги товаров, галереи и раскладку страницы на колонки.

Сетку включают свойством display: grid у родителя. Свойство grid-template-columns описывает столбцы, а дочерние элементы по порядку занимают ячейки.

.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.card {
  padding: 12px;
  border: 1px solid #d0d7de;
  border-radius: 12px;
}
  • Единица fr означает долю свободного места. Значение 1fr 1fr 1fr создаёт три столбца одинаковой ширины, а 2fr 1fr — два столбца, первый из которых вдвое шире.

  • Функция repeat(3, 1fr) — короткая запись для 1fr 1fr 1fr.

  • gap задаёт расстояние между ячейками по горизонтали и вертикали.

Элемент может занимать несколько столбцов: grid-column: span 2 растягивает его на два столбца.

Сетку с фиксированным числом столбцов на узком экране придётся перестраивать. Grid умеет подбирать число столбцов сам:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
}

Такая запись означает: «столько столбцов, сколько поместится, но не уже 240 пикселей каждый». На широком экране получится четыре карточки в ряд, на телефоне — одна.

Почитать ещё

Flexbox или Grid

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

  • Элементы стоят в одну линию, и важно, как они делят место между собой. Выбирайте Flexbox. Примеры: шапка с логотипом и меню, кнопки в ряд, иконка рядом с подписью, подпись рядом с полем формы.

  • Элементы расположены по сетке в двух направлениях, и столбцы должны совпадать по ширине в каждой строке. Выбирайте Grid. Примеры: каталог товаров, галерея, раскладка страницы на основную колонку и боковую панель.

Инструменты часто используют вместе. Каталог выстроен сеткой на Grid, а внутри каждой карточки название и цена стоят в одну строку на Flexbox:

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

.card {
  padding: 12px;
  border: 1px solid #d0d7de;
  border-radius: 12px;
}

.card__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

Если элементы перестают помещаться, сначала проверьте gap и ширину контейнера. Внутри flex-контейнера длинное слово или картинка могут не давать элементу сжиматься. В этом случае помогают flex-wrap: wrap у контейнера или min-width: 0 у элемента.

Почитать ещё

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

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

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

@media (max-width: 600px) {
  .header {
    flex-direction: column;
    gap: 8px;
  }
}

Правило внутри @media (max-width: 600px) действует, только когда ширина окна не больше 600 пикселей. На узком экране меню окажется под логотипом, а на широком останется справа от него.

  • max-width означает «не шире». Такие условия используют, когда основные стили написаны для широкого экрана.

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

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

Чтобы медиазапросы правильно работали на телефоне, в <head> должен быть тег <meta name="viewport" content="width=device-width, initial-scale=1.0">. Без него мобильный браузер считает ширину страницы равной ширине компьютерного экрана.

Проверить страницу на разных размерах экрана можно в режиме эмуляции устройств. Он включается кнопкой с изображением телефона и планшета в левом верхнем углу инструментов разработчика или сочетанием Ctrl+Shift+M (на Mac — Cmd+Shift+M). В этом режиме ширину окна можно задать вручную или выбрать модель телефона из списка.

Почитать ещё

Задания