Flexbox раскладывает элементы в одном направлении — в ряд или в колонку — и распределяет между ними свободное место; им удобно собирать шапку, меню и строку кнопок. Grid работает сразу с колонками и рядами, поэтому подходит для сеток карточек и макета страницы. Адаптивную сетку карточек без медиазапросов даёт одна строка: grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)).
После материала вы сможете
- объяснить разницу
content-boxиborder-boxи посчитать итоговую ширину блока - сверстать шапку на Flexbox с
gap,align-items,flex-wrapи автоматическим отступом - сделать адаптивную сетку карточек через
repeat(auto-fit, minmax(min(100%, 240px), 1fr))и макет с сайдбаром на Grid - найти и исправить переполнение колонки через
minmax(0, 1fr)иmin-width: 0
Проверено на
CSS Flexible Box Layout Level 1CSS Grid Layout Level 1Microsoft Edge 155 + Playwright 1.64W3C CSS Validator (css3svg)
Сверено с документацией: 11 октября 2026 г.
Содержание
- Box model и box-sizing: из чего состоит ширина блока
- Flexbox: оси, выравнивание, gap и перенос
- Grid: колонки, fr, repeat, minmax и auto-fit
- Когда Flex, когда Grid
- Практика: страница из трёх блоков
- Адаптивность без лишних медиазапросов
- Типичные ошибки: переполнение, min-width: 0, проценты
- Упражнения и что дальше
Box model и box-sizing: из чего состоит ширина блока
Прежде чем раскладывать блоки, нужно понимать, какого они размера. Каждый элемент в CSS — прямоугольник из четырёх слоёв: содержимое, внутренний отступ padding, рамка border и внешний отступ margin. По умолчанию (box-sizing: content-box) свойство width задаёт ширину только содержимого, а padding и border прибавляются сверху.
/* width и height включают padding и border */
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.container {
max-width: 1200px;
margin-inline: auto;
padding-inline: 16px;
}Мы измерили в Edge два одинаковых блока с width: 300px, padding: 20px и рамкой 2 px. С content-box блок получился шириной 344 px (300 + 2 × 20 + 2 × 2), с border-box — ровно 300 px. MDN рекомендует border-box для раскладки: размеры становятся предсказуемыми, и width: 100% с отступами больше не вылезает за родителя. Поэтому правило box-sizing: border-box для всех элементов стоит первым почти в любом проекте.
Flexbox: оси, выравнивание, gap и перенос
Flexbox включается одним свойством display: flex на родителе. Родитель становится flex-контейнером, а его прямые потомки — flex-элементами. Всё в Flexbox отсчитывается от двух осей: главной (main axis), её задаёт flex-direction, и поперечной (cross axis), перпендикулярной главной. По умолчанию flex-direction: row, то есть главная ось идёт по строке.
- Элементы стоят в ряд и начинаются от начала главной оси.
- Они не растягиваются по главной оси (
flex-grow: 0), но могут сжиматься (flex-shrink: 1). - По поперечной оси растягиваются на всю высоту ряда (
align-items: stretch). - Не переносятся на новую строку (
flex-wrap: nowrap) — если места не хватает, вылезают за контейнер.
flex-direction
- Где
- контейнер
- Что делает
- Направление главной оси:
row,columnи их-reverse
justify-content
- Где
- контейнер
- Что делает
- Распределение по главной оси:
flex-start,center,space-between…
align-items
- Где
- контейнер
- Что делает
- Выравнивание по поперечной оси:
stretch,center,flex-start…
gap
- Где
- контейнер
- Что делает
- Расстояние между элементами — без margin у каждого и без лишнего отступа у крайних
flex-wrap
- Где
- контейнер
- Что делает
wrapразрешает перенос на новую строку
flex
- Где
- элемент
- Что делает
- Сокращение
flex-grow flex-shrink flex-basis:flex: 1=1 1 0,flex: auto=1 1 auto
Шапка сайта — классическая задача для Flexbox: логотип, меню и кнопка в один ряд, выровненные по центру по вертикали.
/* Шапка: один ряд, Flexbox */
.site-header {
display: flex;
flex-wrap: wrap; /* на узком экране меню уходит на новую строку */
align-items: center; /* выравнивание по поперечной оси */
gap: 12px 24px; /* строки, колонки */
padding: 12px 16px;
border-bottom: 1px solid #ddd;
}
.menu {
display: flex;
gap: 16px;
}
.cta {
margin-inline-start: auto; /* прижимает кнопку к правому краю */
padding: 8px 16px;
border-radius: 8px;
background: #1f2937;
color: #fff;
text-decoration: none;
}align-items: centerвыравнивает логотип, меню и кнопку по вертикальному центру, хотя у кнопки есть свой padding.gap: 12px 24pxзадаёт отступ между рядами и между колонками — он работает и после переноса.margin-inline-start: autoу кнопки забирает всё свободное место перед ней и прижимает её к правому краю. MDN описывает автоматические отступы как способ отделить элемент или группу от остальных.flex-wrap: wrap— страховка для узких экранов: при 375 px шапка в нашей проверке перестроилась в два ряда без единого медиазапроса, при 768 и 1280 px осталась одной строкой.
Grid: колонки, fr, repeat, minmax и auto-fit
Grid — двумерная раскладка: вы описываете колонки (и при необходимости ряды) на контейнере, а элементы встают в ячейки сами. Колонки задаёт grid-template-columns. Помимо пикселей и процентов в Grid есть единица fr — доля свободного места: 1fr 1fr 1fr даёт три равные колонки, 2fr 1fr 1fr делит место на четыре части и отдаёт первой колонке две. Повторы записывают через repeat(): repeat(3, 1fr) — то же, что 1fr 1fr 1fr.
Самое полезное в Grid — сетка, которая сама решает, сколько колонок поместится:
/* Сетка карточек: Grid сам решает, сколько колонок помещается */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
gap: 16px;
padding: 0;
list-style: none;
}
.card {
padding: 16px;
border: 1px solid #ddd;
border-radius: 12px;
}
.card h2 {
margin: 0 0 8px;
font-size: 1.125rem;
}minmax(240px, 1fr)
Колонка не уже 240 px, а всё, что осталось, делится поровну между колонками.
repeat(auto-fit, …)
Браузер ставит столько колонок, сколько помещается.
auto-fitв отличие отauto-fillсхлопывает пустые колонки, и карточки растягиваются на всю ширину.min(100%, 240px)
Страховка для экранов уже 240 px: минимум колонки не может быть больше самого контейнера, и карточка не вылезает за край.
375 px
- Колонок карточек
- 1
- Ширина карточки
- 343 px
768 px
- Колонок карточек
- 2
- Ширина карточки
- 360 px
1280 px
- Колонок карточек
- 4
- Ширина карточки
- 280 px
Разницу auto-fit и auto-fill мы тоже проверили: две карточки в контейнере шириной 1168 px с auto-fit получили по 576 px, а с auto-fill — по 280 px, потому что справа остались пустые колонки. А без min(100%, 240px) карточка в контейнере шириной 200 px сохраняла ширину 240 px и вылезала за край; с ним — сжималась до 200 px.
Когда Flex, когда Grid
MDN формулирует главное различие так: Flexbox создан для раскладки в одном измерении — ряд или колонка, Grid — в двух сразу. Второй критерий оттуда же: Flexbox работает «от содержимого» — размер элементов задаёт их контент, а Grid «от макета» — вы описываете сетку, и элементы встают в неё. Если в Flexbox с переносом элементы второго ряда должны встать ровно под элементами первого, — это задача для Grid: переносимые строки Flexbox независимы и распределяют место каждая сама по себе.
Flexbox
- Шапка, меню, строка кнопок, теги
- Центрирование одного элемента
- Размер элементов зависит от их содержимого
- Прижать элемент к краю через
margin: auto
Grid
- Сетка карточек, галерея, тарифы
- Каркас страницы: контент и сайдбар
- Колонки и ряды должны совпадать
- Адаптивность через
auto-fitиminmax()без медиазапросов
Практика: страница из трёх блоков
Соберём всё вместе: шапка на Flexbox, сетка из шести карточек и блок «контент + сайдбар». Разметка семантическая — как в уроке про HTML; CSS разложен по файлам, которые вы уже видели.
<!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="base.css">
<link rel="stylesheet" href="header.css">
<link rel="stylesheet" href="cards.css">
<link rel="stylesheet" href="layout.css">
</head>
<body>
<header class="site-header">
<a class="logo" href="/">Зерно</a>
<nav class="menu" aria-label="Основное меню">
<a href="#services">Услуги</a>
<a href="#prices">Цены</a>
<a href="#faq">Вопросы</a>
</nav>
<a class="cta" href="#request">Оставить заявку</a>
</header>
<main class="container">
<section id="services">
<h1>Ремонт кофемашин</h1>
<ul class="cards">
<li class="card"><h2>Диагностика</h2><p>Находим причину и согласуем стоимость.</p></li>
<li class="card"><h2>Чистка</h2><p>Удаляем накипь и меняем фильтры.</p></li>
<li class="card"><h2>Ремонт помпы</h2><p>Восстанавливаем давление подачи воды.</p></li>
<li class="card"><h2>Уплотнители</h2><p>Меняем прокладки варочной группы.</p></li>
<li class="card"><h2>Электроника</h2><p>Ремонтируем платы и датчики.</p></li>
<li class="card"><h2>Обслуживание</h2><p>Плановые визиты для кофеен.</p></li>
</ul>
</section>
<div class="with-sidebar">
<article class="content" id="faq">
<h2>Как проходит ремонт</h2>
<p>Мастер приезжает, проводит диагностику и называет стоимость до начала работ.</p>
<pre><code>Статус заявки: https://example.com/requests/2026/10/11/very-long-tracking-identifier-0001</code></pre>
</article>
<aside class="sidebar" id="prices">
<h2>Цены</h2>
<p>Диагностика — бесплатно при ремонте.</p>
</aside>
</div>
</main>
</body>
</html>/* Контент и сайдбар: на телефоне в колонку, с 768px — в две колонки */
.with-sidebar {
display: grid;
grid-template-columns: minmax(0, 1fr); /* одна колонка, которая не растёт от содержимого */
gap: 24px;
margin-block: 32px;
}
@media (min-width: 768px) {
.with-sidebar {
grid-template-columns: minmax(0, 1fr) 280px;
}
}
.content pre {
overflow-x: auto; /* длинная строка прокручивается внутри блока */
padding: 12px;
background: #f3f4f6;
}
.sidebar {
padding: 16px;
border-radius: 12px;
background: #fef3c7;
}Как проверить результат. Откройте страницу в Chrome или Edge, включите DevTools и меняйте ширину окна. В панели Elements рядом с контейнерами появятся значки grid и flex — клик по значку рисует на странице линии сетки и границы flex-элементов. Мы проверили то же программно через getBoundingClientRect() на трёх ширинах:
375 px
- Шапка
- 2 ряда
- Карточки
- 1 колонка
- Сайдбар
- под контентом
- Горизонтальная прокрутка
- нет
768 px
- Шапка
- 1 ряд
- Карточки
- 2 колонки
- Сайдбар
- справа, 280 px
- Горизонтальная прокрутка
- нет
1280 px
- Шапка
- 1 ряд
- Карточки
- 4 колонки
- Сайдбар
- справа, 280 px
- Горизонтальная прокрутка
- нет
Адаптивность без лишних медиазапросов
Медиазапрос (@media) включает правила при определённой ширине окна. Он нужен, когда макет действительно меняет структуру — как сайдбар, который на телефоне уходит под контент. Но большинство перестроений Flexbox и Grid делают сами, если не мешать им фиксированными ширинами:
flex-wrap: wrapпереносит элементы, когда им тесно, — шапке не нужен отдельный брейкпоинт.repeat(auto-fit, minmax(…))сам считает число колонок — сетке карточек не нужны четыре медиазапроса под каждый экран.max-widthвместоwidthу контейнера: на широком экране он ограничен 1200 px, на узком занимает всю ширину.- Единицы
frи проценты вместо пикселей у колонок,gapвместоmarginу каждого элемента.
Подробнее о брейкпоинтах и mobile-first с точки зрения дизайна — в материале об адаптивном дизайне.
Типичные ошибки: переполнение, min-width: 0, проценты
Три ошибки встречаются чаще остальных. Все три мы воспроизвели на отдельной странице с контейнером шириной 760 px:
.demo { width: 760px; margin-bottom: 32px; outline: 1px dashed #999; }
/* 1. box-sizing: одна и та же ширина — разный итог */
.box { width: 300px; padding: 20px; border: 2px solid; }
.box-content { box-sizing: content-box; } /* 300 + 2 × 20 + 2 × 2 = 344px */
.box-border { box-sizing: border-box; } /* ровно 300px */
/* 2. Проценты + gap: три по 33.333% не помещаются в ряд */
.row-percent { display: flex; flex-wrap: wrap; gap: 16px; }
.row-percent > * { width: 33.333%; }
.row-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
/* 3. Длинное содержимое распирает колонку 1fr */
.layout-bad { display: grid; grid-template-columns: 1fr 280px; gap: 24px; }
.layout-good { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 24px; }
/* то же во Flexbox: у flex-элемента min-width: auto */
.flex-bad { display: flex; gap: 24px; }
.flex-bad > .main { flex: 1; }
.flex-good { display: flex; gap: 24px; }
.flex-good > .main { flex: 1; min-width: 0; }
.flex-bad > .side, .flex-good > .side { flex: 0 0 280px; }
pre { overflow-x: auto; margin: 0; }content-box по умолчанию
- Что измерили
width: 300pxдал блок 344 px- Исправление
box-sizing: border-box
Три элемента по 33.333% и gap: 16px во Flexbox
- Что измерили
- Третий элемент перенёсся на второй ряд: 100% ширины + 32 px отступов не помещаются
- Исправление
grid-template-columns: repeat(3, 1fr)—frделит место уже после вычетаgap
Длинная строка в колонке 1fr
- Что измерили
- Колонка растянулась, сайдбар уехал до 1006 px при контейнере 760 px
- Исправление
minmax(0, 1fr)вместо1fr
Длинная строка во flex-элементе с flex: 1
- Что измерили
- Тот же выход за контейнер до 1006 px
- Исправление
min-width: 0у flex-элемента
Почему 1fr и flex: 1 не сжимаются? Спецификация Flexbox задаёт flex-элементам автоматический минимальный размер: по умолчанию min-width: auto, и элемент не становится уже своего содержимого — самого длинного слова, картинки или строки в pre. У элементов Grid то же поведение, а 1fr по web.dev — это minmax(auto, 1fr). Явный минимум 0 снимает ограничение, и тогда overflow-x: auto у pre прокручивает строку внутри блока, не ломая страницу.
Ещё одно предупреждение — о порядке. Grid и свойство order позволяют поставить элемент визуально в любое место, но MDN напоминает: скринридер читает и клавиша Tab обходит элементы в порядке HTML. Если визуальный порядок сильно расходится с исходным, пользователь клавиатуры будет прыгать по странице. Меняйте порядок в разметке, а не только в CSS.
Упражнения и что дальше
Дальше по траектории — JavaScript в браузере: как найти элементы этой страницы из кода, менять их и реагировать на клики. Если в проекте используется Tailwind CSS, те же flex, grid, gap и minmax там записываются утилитами — механика раскладки остаётся этой.
Как проверено5 · пройденосверено 11 октября 2026 г.
- запущено, работает
base.css, header.css, cards.css, layout.css, index.html (RU и EN)
Microsoft Edge 155 через Playwright 1.64, viewport 375/768/1280: getBoundingClientRect — карточек в ряду 1/2/4 (343/360/280 px), шапка 2/1/1 ряда, кнопка прижата к правому краю, сайдбар под контентом / справа 280px, нет горизонтальной прокрутки, pre не выходит за колонку, нет ошибок в консоли; скриншоты на трёх ширинах. html-validate 11.16.2 и Nu Html Checker — 0 замечаний (require-sri выключен: локальные стили); W3C CSS Validator — 0 ошибок, 0 предупреждений
- запущено, работает
pitfalls.css + pitfalls.html
Edge 155: content-box 344px / border-box 300px; 33.333% + gap — 2 ряда, repeat(3, 1fr) — 1 ряд; Grid 1fr и flex: 1 — сайдбар до 1006px (RU) / 947px (EN) при контейнере 760px, minmax(0, 1fr) и min-width: 0 — 760px
- запущено, работает
min(100%, 240px) и auto-fit/auto-fill
Edge 155: в контейнере 200px карточка 240px без min() и 200px с min(); две карточки в 1168px — 576px (auto-fit) и 280px (auto-fill)
- запущено, работает
решения упражнений (300px, sidebar-left.css, pricing.css)
Edge 155: 3 колонки при 300px на 1280; сайдбар слева 280px на 1280 и под контентом на 375; кнопки тарифов на одной линии, 17px от низа, ширина по тексту на 375 и 1280
- запущено, работает
первая версия layout.css (без grid-template-columns в базовом правиле)
реальная ошибка, найденная проверкой: при 375px колонка 718px и горизонтальная прокрутка; исправлено minmax(0, 1fr), описано в статье
«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.