Skip to Content

Лекция 04. CSS: построение интерфейсов — Flexbox, Grid и позиционирование

В прошлой лекции мы научились оформлять элементы и впервые использовали Flexbox и Grid. Теперь разберёмся, как браузер выбирает место и размер каждого блока. Тогда строка display: flex перестанет быть заклинанием, а для выравнивания кнопки не придётся подбирать случайный margin-left.

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

После этой лекции

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

Адаптацию под разные экраны, состояния элементов и анимацию подробно рассмотрим на следующей лекции. Здесь сосредоточимся на устройстве раскладки.


1. Сначала — обычный поток

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

<section class="intro"> <h1>Учебные материалы</h1> <p>Начните с <a href="#html">основ HTML</a>, затем переходите к CSS.</p> <p>Каждая тема содержит примеры.</p> </section>

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

Свойство display определяет, как элемент участвует в раскладке. Для начала нужны четыре значения:

ЗначениеЧто увидим в обычном потокеГде применяем
blockОтдельный блок; при width: auto обычно заполняет доступную ширину родителяРаздел, абзац, крупная ссылка
inlineЧасть текстовой строки; обычные width и height для такой текстовой коробки не задают размерСсылка в предложении, выделенное слово
inline-blockЦельная коробка внутри строки; можно задать ширину и высотуНебольшая метка или ссылка с оформлением кнопки
noneЭлемент и его потомки не занимают места и не отображаютсяСкрытая часть интерфейса

display: none также обычно убирает содержание из дерева доступности: это не способ оставить невидимое пояснение для экранного диктора. Подробнее поведение display описано в спецификации CSS Display .

Проверим на маленьком примере:

<p>Доступен <a class="download" href="guide.pdf">конспект</a> по теме.</p>
.download { display: inline-block; padding: 8px 12px; background: #e8f0fe; }

Ссылка станет цельной плашкой с внутренним отступом, но останется в строке текста. Замените inline-block на block — она займёт отдельную строку и при автоматической ширине растянется по доступному месту. Замените на none — исчезнет и ссылка, и её место.

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

2. Flexbox: собираем шапку

Задача: название сайта поставить слева, навигацию — справа, а по высоте выровнять их центры.

<header class="site-header"> <a class="brand" href="index.html">Веб-курс</a> <nav class="site-nav" aria-label="Основная навигация"> <a href="#materials">Материалы</a> <a href="#about">О курсе</a> </nav> </header>
.site-header { display: flex; justify-content: space-between; align-items: center; gap: 24px; padding: 20px; background: #e8f0fe; } .site-nav { display: flex; gap: 16px; }

Получится такая структура:

┌─────────────────────────────────────────────────────┐ │ Веб-курс Материалы О курсе │ └─────────────────────────────────────────────────────┘

Шапка — flex-контейнер. Её непосредственные дети, .brand и .site-nav, — flex-элементы. Контейнер управляет их совместным расположением.

Ссылки внутри nav не становятся flex-элементами шапки: между ними и header есть родитель nav. Поэтому мы отдельно назначили display: flex самому nav. Получились два независимых уровня: шапка расставляет название и навигацию, навигация — свои ссылки.

Это правило важно и для Grid: раскладка контейнера действует на его непосредственных детей, а не на всех потомков сразу. Если обернуть несколько карточек дополнительным div, для внешнего контейнера этот div станет одним элементом. Основы модели разобраны в руководстве MDN по Flexbox .

Две оси вместо «горизонтально» и «вертикально»

По умолчанию действует flex-direction: row: основная ось проходит вдоль строки. Поперечная ось перпендикулярна ей.

row: основная → поперечная ↓ column: основная ↓ поперечная →

justify-content распределяет элементы по основной оси, align-items выравнивает их по поперечной.

В нашей шапке space-between отправляет первый элемент к началу строки, последний — к концу, а свободное место размещает между ними. align-items: center совмещает центры по высоте. Оно особенно заметно, если название написано крупнее ссылок.

Измените одну строку: добавьте шапке flex-direction: column. Название окажется над навигацией. Теперь align-items: center центрирует их по горизонтали, а justify-content работает по вертикали. Если высота шапки определяется содержимым, большого свободного места по вертикали нет — и space-between почти нечего распределять.

Отсюда частая ошибка: «justify-content: center не центрирует по горизонтали». Сначала проверьте направление оси, затем — есть ли свободное место вдоль неё.

Промежутки и свободное место — разные вещи

gap: 24px задаёт промежуток между соседями. Он не создаёт отступ от края контейнера: за него отвечает padding.

justify-content распоряжается местом, которое осталось после размеров элементов и промежутков. Для повседневной работы достаточно понимать:

  • flex-start — собрать элементы у начала основной оси;
  • center — собрать в центре;
  • flex-end — собрать у конца;
  • space-between — распределить свободное место между элементами.

Если заменить в шапке space-between на flex-start, название и навигация соберутся слева, но промежуток 24px сохранится. Если свободного места нет, space-between не сможет создать его из ничего.

У align-items часто нужны stretch, center и flex-start. По умолчанию stretch растягивает элементы по поперечной оси, когда их размер в этом направлении автоматический. Для шапки обычно удобнее center, для карточек одинаковой высоты — растягивание.

Что делать, если ссылок стало больше

По умолчанию действует flex-wrap: nowrap: элементы пытаются остаться в одной строке. Если добавить ещё пять ссылок, места может не хватить.

.site-header, .site-nav { flex-wrap: wrap; }

Теперь навигация может целиком перейти под название, а её ссылки — переноситься внутри самого nav. Каждая новая flex-строка распределяет место самостоятельно. Поэтому Flexbox с переносом не гарантирует общих колонок для всех строк. Если края карточек должны совпадать сверху вниз, удобнее Grid.

3. Как Flexbox делит ширину

До сих пор мы управляли контейнером. Теперь решим задачу для самих элементов: в строке есть поле поиска и кнопка. Кнопке достаточно её естественной ширины, поле должно занять остальное место.

<form class="search" action="/search" method="get"> <label for="query">Найти тему</label> <input id="query" name="q" type="search"> <button type="submit">Найти</button> </form>
.search { display: flex; align-items: center; gap: 12px; } .search input { flex: 1 1 160px; min-width: 0; } .search button { flex: 0 0 auto; }

Поле займёт свободную часть строки. Надпись и кнопка останутся рядом. Здесь форма нужна для демонстрации раскладки; адрес /search должен обрабатываться сайтом, чтобы поиск действительно выполнялся.

Запись flex: 1 1 160px объединяет три свойства:

.search input { flex-grow: 1; flex-shrink: 1; flex-basis: 160px; }

Читайте её по порядку: можно расти; можно сжиматься; исходный размер — 160px вдоль основной оси.

flex-basis — отправная точка расчёта, а не обещание итоговой ширины. flex-grow задаёт долю прибавки, когда есть свободное место. flex-shrink участвует в распределении нехватки места. У обычного flex-элемента начальные значения — 0 1 auto: сам не растёт, но может сжиматься; исходный размер берётся из заданного размера по основной оси или из содержимого.

Представим отдельный ряд: два простых блока без рамок и отступов, у каждого flex-basis: 200px. Ширина контейнера — 600px, gap: 20px. После базовых размеров и промежутка осталось 180px.

  • При flex-grow: 1 у обоих каждый получит ещё 90px: итог — 290px и 290px.
  • При коэффициентах 1 и 2 прибавки будут 60px и 120px: итог — 260px и 320px.

Коэффициент 2 не означает «весь элемент вдвое шире». Он означает вдвое большую долю прибавки в этом примере.

Если место заканчивается, сжатие учитывает и flex-shrink, и исходный размер: при одинаковом коэффициенте более крупный элемент обычно отдаёт больше пикселей. Запоминать формулу сейчас не нужно; важно не считать width или flex-basis безусловно фиксированной шириной внутри Flexbox. Правила расчёта определены в спецификации Flexbox .

У кнопки flex: 0 0 auto: не растёт, не сжимается, размер определяется её содержимым и оформлением. Если заменить первое число у поля на 0, оно перестанет забирать свободное место. Если поставить flex-shrink: 0 всем элементам, при нехватке ширины они могут выйти за край контейнера.

Почему элемент иногда отказывается сжиматься

Автоматический минимальный размер flex-элемента может ограничиваться его содержимым. Даже flex-shrink: 1 не всегда позволяет сделать его достаточно узким. Поэтому для гибкой области часто задают min-width: 0: мы разрешаем ей стать уже этого автоматического минимума.

Но это не переносит длинный текст автоматически. Если внутри обычного блока есть длинная непрерывная строка, отдельно может понадобиться overflow-wrap: anywhere. У поля ввода длинное значение прокручивается внутри поля. Ограничения содержимого и варианты сокращённой записи разобраны в MDN: свойство flex .

4. Grid: раскладываем карточки по общим колонкам

Задача: показать материалы в двух одинаковых колонках; следующие карточки должны попадать под предыдущие.

<div class="materials"> <article class="card"><h2>HTML</h2><p>Структура страницы.</p></article> <article class="card"><h2>CSS</h2><p>Оформление и раскладка.</p></article> <article class="card"><h2>Практика</h2><p>Собираем страницу.</p></article> </div>
.materials { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .card { padding: 20px; border: 1px solid #cbd5e1; }

Первые две карточки займут первую строку, третья — первую ячейку второй. Добавьте четвёртую — она встанет рядом с третьей. Браузер сам создаёт недостающие строки.

Grid-контейнер — .materials, grid-элементы — три article. Колонки и строки называют треками, их границы — линиями сетки. В отличие от переноса во Flexbox, все строки пользуются общей сеткой колонок.

Что означает fr

fr — доля пространства, распределяемого между гибкими треками. В нашем простом примере после вычитания промежутка оставшаяся ширина делится поровну. При ширине контейнера 620px и gap: 20px колонки получат по 300px.

Поменяйте правило на grid-template-columns: 2fr 1fr: первая колонка получит две доли, вторая — одну. В том же примере это 400px и 200px, пока минимальные размеры содержимого не мешают такому распределению.

Для боковой панели и основной области можно написать 240px 1fr: сначала выделить панели 240px, учесть промежуток, затем отдать оставшееся гибкой колонке. fr удобнее процентов: две колонки по 50% уже занимают всю ширину, а дополнительный gap может создать переполнение. Подробности — в описании grid-template-columns на MDN .

repeat() и minmax() без сложной формулы

Одинаковые колонки удобно записывать так:

.materials { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }

Прочитаем изнутри наружу:

  1. minmax(0, 1fr) — гибкая колонка с разрешённым минимумом 0 и распределением места через 1fr.
  2. repeat(2, ...) — повторить такую колонку два раза.

Почему не просто 1fr? У него есть автоматический минимум, связанный с содержимым. Очень длинное слово может мешать колонке сжиматься. minmax(0, 1fr) снимает именно это ограничение трека; длинному тексту всё ещё нужны правила переноса. Поэтому в итоговом примере карточкам добавим overflow-wrap: anywhere.

minmax(220px, 1fr) задаёт другой смысл: колонка не должна становиться уже 220px, но может расти. Две такие колонки с промежутком 20px потребуют минимум 460px. Эта запись сама по себе не уменьшает количество колонок. Не путайте нижний предел размера с переключением раскладки.

Строки: не задавайте высоту без необходимости

По умолчанию новые строки подстраиваются под содержание. Высоту строки определяет самая высокая карточка в ней; соседние карточки обычно растягиваются до этой высоты. Вторая строка может иметь другую высоту.

grid-template-rows задаёт строки явно. Например, grid-template-rows: auto auto описывает две строки по содержимому. Но в нашем каталоге это лишнее: количество материалов меняется, и браузер уже создаёт строки автоматически.

Если поставить grid-template-rows: 100px 100px, длинный текст не обязан уместиться: он может выйти за рамку карточки. Для карточек с неизвестным заранее текстом начните с автоматической высоты. Фиксированные размеры нужны тогда, когда этого требует сама задача, а не для борьбы с разной длиной текста.

Одна карточка на всю ширину

Сделаем первый материал вводным и выделим ему обе колонки:

<article class="card card--wide"> <h2>С чего начать</h2> <p>Прочитайте введение, затем переходите к отдельным темам.</p> </article>
.card--wide { grid-column: 1 / -1; }

grid-column задаётся элементу. 1 — первая линия, -1 — последняя линия явно заданной сетки. У двух колонок три линии: левая граница, граница между колонками и правая граница. Карточка растянется от первой до последней, а следующие займут строку ниже.

grid-column: span 2 означает занять две колонки от места, которое найдёт автоматическое размещение. Это полезно, но 1 / -1 точнее выражает намерение «на всю заданную ширину сетки». Линии и размещение определены в спецификации Grid .

Сохраняйте разумный порядок карточек в HTML. Визуальная перестановка средствами CSS не переписывает порядок чтения документа и перемещения клавишей Tab.

5. Позиционирование: привязываем метку к карточке

Flexbox и Grid распределяют место между соседями. Но у метки «Новое» другая задача: находиться в правом верхнем углу конкретной карточки.

<article class="card card--marked"> <span class="badge">Новое</span> <h2>CSS: раскладка</h2> <p>Flexbox, Grid и позиционирование.</p> </article>
.card--marked { position: relative; padding-top: 56px; } .badge { position: absolute; top: 12px; right: 12px; padding: 4px 8px; background: #0757c7; color: white; }

Метка окажется внутри карточки, в 12px от её верхнего и правого краёв. Разберём обе половины решения.

position: relative оставляет карточку в потоке на прежнем месте и создаёт удобную точку отсчёта для абсолютно позиционированного потомка. Без смещений сама карточка не двигается.

position: absolute убирает метку из нормального потока. Соседи больше не резервируют для неё место, а top и right задают расстояния от соответствующих краёв её содержащего блока — области, относительно которой рассчитывается положение. В этом примере её задаёт ближайший предок с position, отличным от static: карточка.

Поэтому мы увеличили padding-top: метка перестала занимать место, и отступ вручную оставляет для неё верхнюю полосу. Это подходит для короткой метки. Если подпись может стать длинной и многострочной, часто лучше оставить её в обычном потоке, чтобы высота учитывалась автоматически.

Эксперимент: уберите у карточки position: relative. Метка будет искать другую точку отсчёта выше по дереву и может уехать в угол страницы. Верните правило — привязка восстановится.

Не выбирайте absolute для всех карточек каталога. Добавленный текст перестанет раздвигать соседей, и блоки начнут перекрывать друг друга.

static и relative: место сохраняется

static — обычное значение position. Свойства top, right, bottom, left для такого элемента не задают смещение.

У relative те же свойства сдвигают элемент от его собственного обычного положения:

.shifted { position: relative; top: 10px; left: 20px; }

Элемент будет нарисован на 10px ниже и 20px правее, но его прежнее место останется занятым. Соседи не подвинутся вслед за ним; возможно наложение. Поэтому такие смещения не заменяют gap, padding и раскладку.

top: 10px у relative — сдвиг вниз от обычного места; у absolute — расстояние от верхнего края содержащего блока. Одинаковое свойство читается с учётом режима позиционирования. Эти режимы и смещения описаны в MDN: position .

fixed: закрепление в окне

Допустим, нужна ссылка возврата к началу страницы:

<a class="back-to-top" href="#top">Наверх</a>
.back-to-top { position: fixed; right: 20px; bottom: 20px; padding: 12px; background: white; border: 1px solid #cbd5e1; }

Если у начала страницы есть id="top", ссылка ведёт к нему. При прокрутке сама ссылка остаётся в правом нижнем углу окна. Как и absolute, она не резервирует место в потоке и может закрыть текст. Проверяйте это, особенно при увеличении масштаба.

В обычном случае fixed привязан к окну просмотра. У absolute и fixed есть исключения: например, предок с transform, отличным от none, тоже может создать содержащий блок. Для наших примеров достаточно обычной модели; если привязка неожиданна, проверяйте свойства предков, а не только самого элемента.

sticky: сначала в потоке, затем у границы

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

.site-header { position: sticky; top: 0; z-index: 10; background: #e8f0fe; }

Шапка сначала занимает обычное место. Когда при прокрутке её верх достигнет заданной границы top: 0, она останется у неё. В отличие от fixed, её место в раскладке сохраняется. Фон нужен, чтобы прокручиваемый текст не просвечивал сквозь шапку.

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

sticky ограничен своим содержащим блоком и ориентируется на ближайшего предка с механизмом прокрутки. Поэтому короткий родитель, отсутствие прокрутки или overflow: auto, scroll либо hidden у предка могут объяснить, почему элемент не прилипает к окну так, как вы ожидали. Не помещайте такую шапку в обёртку высотой ровно с саму шапку: ей негде будет двигаться.

z-index: кто находится сверху

Когда блоки перекрываются, нужен порядок слоёв. В примере z-index: 10 помогает шапке оказаться выше обычного содержания. Чем больше значение, тем выше элемент в пределах одного контекста наложения.

Контекст наложения можно представить как отдельную группу слоёв. Если вся группа находится ниже другой группы, большое число у её ребёнка не вытащит ребёнка поверх чужой группы. Такие группы создают, например, position: relative вместе с числовым z-index, а также sticky и fixed.

Не увеличивайте число до 999999, если ничего не изменилось: посмотрите на родителей. z-index применяется к позиционированным элементам, а также к flex- и grid-элементам даже без изменения position. Подробности и примеры групп — в MDN: контекст наложения .

6. Собираем вместе: Grid снаружи, Flexbox внутри

Теперь соберём небольшой законченный пример. Сохраните два файла рядом: index.html и styles.css. Откройте HTML в браузере. Здесь нет внешних изображений или библиотек.

Шапка использует Flexbox; материалы размещаются в Grid. Каждая карточка одновременно является grid-элементом внешней сетки и flex-контейнером для своего содержания. Это нормальное сочетание: родитель определяет место карточки, сама карточка — расположение своих детей.

index.html
<!doctype html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Материалы веб-курса</title> <link rel="stylesheet" href="styles.css"> </head> <body id="top"> <header class="site-header"> <a class="brand" href="#top">Веб-курс</a> <nav class="site-nav" aria-label="Основная навигация"> <a href="#materials">Материалы</a> <a href="#about">О курсе</a> </nav> </header> <main class="page"> <h1>Учебные материалы</h1> <p>От структуры документа к раскладке интерфейса.</p> <section id="materials" aria-labelledby="materials-title"> <h2 id="materials-title">Темы курса</h2> <div class="materials"> <article class="card card--wide"> <h3>Как пользоваться материалами</h3> <p>Читайте объяснение, запускайте пример и меняйте одно свойство за раз. Перед запуском попробуйте предсказать результат.</p> </article> <article class="card"> <h3>HTML: структура</h3> <p>Заголовки, абзацы, ссылки и смысловая разметка.</p> <a class="card-link" href="#html-note">К пояснению об HTML</a> </article> <article class="card card--marked"> <span class="badge">Новое</span> <h3>CSS: раскладка</h3> <p>Разберём, как распределить пространство между элементами, согласовать колонки и закрепить метку в углу карточки.</p> <a class="card-link" href="#css-note">К пояснению о CSS</a> </article> </div> </section> <section id="about"> <h2>О курсе</h2> <p>Сначала создаём понятный документ, затем управляем его представлением.</p> <h3 id="html-note">HTML задаёт смысл</h3> <p>Карточка остаётся статьёй, а переход — ссылкой даже без стилей.</p> <h3 id="css-note">CSS задаёт расположение</h3> <p>Grid управляет карточками, Flexbox — содержимым каждой карточки.</p> </section> </main> </body> </html>
styles.css
* { box-sizing: border-box; } body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.6; color: #172033; background: #f5f7fb; } a { color: #0757c7; } .site-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 16px; padding: 20px; border-bottom: 1px solid #cbd5e1; background: #e8f0fe; } .brand { font-size: 1.25rem; font-weight: 700; } .site-nav { display: flex; flex-wrap: wrap; gap: 16px; } .page { max-width: 1000px; margin: 0 auto; padding: 24px; } .materials { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; } .card { display: flex; flex-direction: column; gap: 12px; padding: 20px; border: 1px solid #cbd5e1; border-radius: 12px; background: white; overflow-wrap: anywhere; } .card h3, .card p { margin: 0; } .card--wide { grid-column: 1 / -1; } .card--marked { position: relative; padding-top: 56px; } .badge { position: absolute; top: 12px; right: 12px; padding: 4px 8px; border-radius: 6px; background: #0757c7; color: white; font-size: 0.875rem; } .card-link { margin-top: auto; align-self: flex-start; }

Вы увидите шапку, вводную карточку на всю ширину и две карточки под ней. В нижней паре высота будет общей благодаря Grid, даже если текста в одной больше. Ссылки окажутся у нижнего края своих карточек.

За положение ссылок отвечает margin-top: auto. В колонке Flexbox автоматический верхний отступ забирает оставшееся свободное место перед ссылкой и отталкивает её вниз. У более короткой карточки этот отступ будет больше. align-self: flex-start отдельно выравнивает ссылку у начала поперечной оси: она не растягивается на всю ширину. Это исключение для одного элемента, тогда как align-items на контейнере действует на всех его детей.

У заголовка и абзаца убраны стандартные margin, чтобы расстояния внутри карточки задавал один понятный gap. Шапка в полном примере остаётся в обычном потоке: правило со sticky из предыдущего раздела можно добавить отдельно. Двухколоночный вариант предназначен для разбора механики; выбор числа колонок для разных экранов разберём на следующей лекции.

Проведите три опыта:

  1. Добавьте в HTML-карточку ещё один абзац. Вторая карточка той же строки станет такой же высоты, а ссылки сохранят нижнее выравнивание.
  2. Уберите margin-top: auto у .card-link. Ссылка пойдёт сразу после текста, и свободное место останется под ней.
  3. Замените repeat(2, ...) на repeat(3, ...) и добавьте третью обычную карточку. Получите три колонки под вводной, которая по-прежнему занимает всю ширину.

7. Как найти причину, если раскладка не совпала с ожиданием

Начните с HTML: кто родитель и кто его непосредственные дети? Затем откройте DevTools и включите подсветку flex или grid рядом с нужным контейнером. Она покажет реальные границы и промежутки.

СимптомЧто проверить первым
justify-content почти ничего не меняетОсновную ось и наличие свободного места
Ссылки не встали в строкуdisplay: flex у их непосредственного родителя
Элемент с заданной шириной стал ужеСжатие во Flexbox: flex-shrink и flex-basis
Длинная строка растянула раскладкуМинимальный размер элемента/трека и перенос текста
Метка оказалась в углу страницыposition: relative у нужной карточки
Содержание налезло на соседнееНе выведены ли основные блоки из потока; нет ли фиксированной высоты
sticky не удерживается у окнаПорог top, высоту родителя и overflow предков
Большой z-index не помогКонтексты наложения у родителей

Проверяйте по одной гипотезе: изменили одно правило, посмотрели результат, объяснили причину. Так вы находите механизм ошибки, а не случайную комбинацию значений.

8. Проверьте понимание

Сначала ответьте самостоятельно, затем прочитайте объяснения ниже.

  1. В контейнере display: flex; flex-direction: column нужно поставить детей по центру по горизонтали. Какое свойство применить?
  2. У .materials стоит display: grid, но все карточки обёрнуты одним общим div. Почему сетка не распределяет сами карточки?
  3. У двух элементов одинаковый flex-basis, а flex-grow равен 1 и 2. Второй обязательно будет вдвое шире первого?
  4. Для двух колонок задали 50% 50% и gap: 20px. Почему появился выход за край и как исправить?
  5. Для метки задали position: absolute, но заголовок начал заходить под неё. Кто должен зарезервировать место?
  6. Когда для шапки выбрать sticky, а когда fixed?

Ответы с объяснениями

  1. align-items: center: у column поперечная ось горизонтальная. justify-content в этом случае работает по вертикали.
  2. Grid видит один непосредственный дочерний div и размещает его как один элемент. Нужно убрать ненужную обёртку либо сделать grid-контейнером её саму.
  3. Нет. При наличии свободного места второй получает вдвое большую прибавку. К базовому размеру каждого она добавляется отдельно.
  4. Проценты уже заняли всю ширину, промежуток добавился сверх неё. Для равных гибких колонок подойдёт repeat(2, minmax(0, 1fr)): промежуток учитывается при распределении пространства.
  5. absolute не резервирует место в потоке. Для короткой метки можно оставить полосу через padding-top у карточки, как в примере. Если высота метки непредсказуема, разумнее вернуть её в поток.
  6. sticky — если шапка должна сохранить своё место и удерживаться у границы при прокрутке в пределах родителя. fixed — если элемент должен быть привязан к окну и не участвовать в потоке; тогда перекрытие содержания нужно учитывать отдельно.

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

Обновлено