Как делать11 октября 2026 г.8 мин чтения
Вёрстка: HTML и CSSШаг 3 из 7

CSS-раскладка: Flexbox и Grid на реальных блоках

Коротко

Flexbox раскладывает элементы в одном направлении — в ряд или в колонку — и распределяет между ними свободное место; им удобно собирать шапку, меню и строку кнопок. Grid работает сразу с колонками и рядами, поэтому подходит для сеток карточек и макета страницы. Адаптивную сетку карточек без медиазапросов даёт одна строка: grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)).

Пошаговый урокУровень: НачальныйCSSПрактика: ~45–90 мин

После материала вы сможете

  • объяснить разницу 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 г.

Содержание
  1. 01Box model и box-sizing: из чего состоит ширина блока
  2. 02Flexbox: оси, выравнивание, gap и перенос
  3. 03Grid: колонки, fr, repeat, minmax и auto-fit
  4. 04Когда Flex, когда Grid
  5. 05Практика: страница из трёх блоков
  6. 06Адаптивность без лишних медиазапросов
  7. 07Типичные ошибки: переполнение, min-width: 0, проценты
  8. 08Упражнения и что дальше

Box model и box-sizing: из чего состоит ширина блока

Прежде чем раскладывать блоки, нужно понимать, какого они размера. Каждый элемент в CSS — прямоугольник из четырёх слоёв: содержимое, внутренний отступ padding, рамка border и внешний отступ margin. По умолчанию (box-sizing: content-box) свойство width задаёт ширину только содержимого, а padding и border прибавляются сверху.

base.css
/* 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;
}
Базовые стили демо-страницы: box-sizing для всех элементов и центрированный контейнер.

Мы измерили в 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: логотип, меню и кнопка в один ряд, выровненные по центру по вертикали.

header.css
/* Шапка: один ряд, 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 — сетка, которая сама решает, сколько колонок поместится:

cards.css
/* Сетка карточек: 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;
}
Сетка карточек услуг: число колонок зависит от ширины контейнера, медиазапросов нет.
  1. minmax(240px, 1fr)

    Колонка не уже 240 px, а всё, что осталось, делится поровну между колонками.

  2. repeat(auto-fit, …)

    Браузер ставит столько колонок, сколько помещается. auto-fit в отличие от auto-fill схлопывает пустые колонки, и карточки растягиваются на всю ширину.

  3. min(100%, 240px)

    Страховка для экранов уже 240 px: минимум колонки не может быть больше самого контейнера, и карточка не вылезает за край.

375 px

Колонок карточек
1
Ширина карточки
343 px

768 px

Колонок карточек
2
Ширина карточки
360 px

1280 px

Колонок карточек
4
Ширина карточки
280 px
Измерено в Edge 155 на демо-странице с контейнером до 1200 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 разложен по файлам, которые вы уже видели.

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="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>
Демо-страница: четыре CSS-файла подключены по порядку, длинная ссылка в pre нужна для проверки переполнения.
layout.css
/* Контент и сайдбар: на телефоне в колонку, с 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:

pitfalls.css
.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.

Расскажите о задаче — мы изучим запрос и свяжемся с вами

Есть задача?
Обсудим и оценим — бесплатно.

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

  • Оценка объёма и рисков до старта
  • NDA — по вашему или нашему шаблону
  • Старт работ в течение недели

Не присылайте через форму пароли, ключи доступа и исходный код — безопасную передачу доступа обсудим отдельно.

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