Flexbox и CSS Grid: раскладка сайта
Flexbox и CSS Grid — две системы раскладки современного CSS. Flexbox выстраивает элементы в одну линию и идеально подходит для панелей и рядов, а Grid строит двухмерную сетку строк и колонок — каркас целой страницы. В этом уроке разберём обе системы, потренируемся на живых примерах и научимся делать адаптивные макеты.
1. Одна ось усиливает, две — побеждают: когда flex, когда grid
Перед тем как писать код, пойми ключевое различие. Flexbox — одномерная раскладка: он работает с одним направлением — рядом или колонкой. Grid — двухмерная раскладка: он управляет колонками и рядами одновременно.
| Flexbox | Grid |
|---|---|
| одна ось | две оси: колонки и ряды |
| ряд или колонка | полноценная сетка |
| размер элементов зависит от содержимого | размер зависит от ячеек сетки |
| панели, шапки, кнопки, карточки в ряд | каркас страницы, галереи, дашборды |
Практическое правило: flex — для панелей, grid — для страниц. Если нужно выстроить несколько элементов в линию и не важно, как разделится место между ними, — бери flexbox. Если раскладываешь контент по строкам и колонкам одновременно — бери grid.
/* каркас страницы — grid */
.page {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-areas: "header header" "sidebar main" "footer footer";
}
/* внутренности ячеек — flex */
.toolbar { display: flex; gap: 12px; }2. Flexbox: оси и главный контейнер
Flexbox включается свойством display: flex на родительском элементе — он становится flex-контейнером, а его прямые дети — flex-элементами. Дальше контейнер решает, как расположить детей.
.toolbar {
display: flex;
/* row — элементы в ряд (по умолчанию), column — в колонку */
flex-direction: row;
}У flex-контейнера две оси:
- main axis (главная ось) — направление раскладки. При
flex-direction: rowона горизонтальная, приcolumn— вертикальная; - cross axis (поперечная ось) — перпендикулярна главной.
Смена направления через flex-direction: row-reverse или column-reverse разворачивает порядок элементов — удобно, когда нужно зеркалировать меню без правки разметки.
.menu {
display: flex;
flex-direction: row-reverse;
/* пункты пойдут справа налево */
}display: inline-flex. Контейнер останется flex, но сам поведёт себя как строчный элемент: его ширина будет равна содержимому и он встанет в поток текста. Полезно для маленьких лейблов и чипов.3. Выравнивание в flex
Главные свойства выравнивания в flexbox — по паре на каждую ось. justify-content распределяет элементы вдоль главной оси, align-items — вдоль поперечной.
.toolbar {
display: flex;
/* главная ось: от начала до конца, равномерно, с пробелами… */
justify-content: start | center | space-between | space-around | space-evenly;
/* поперечная ось: растянуть, по центру, к краям… */
align-items: stretch | center | flex-start | flex-end;
}space-between— крайние элементы прижаты к краям, остальные — равномерно между ними;space-around— одинаковый зазор вокруг каждого элемента (у крайних — меньше);space-evenly— одинаковое расстояние между всеми, включая края.
Отдельный элемент можно выравнивать независимо от остальных через align-self. Классическая задача — шапка сайта: логотип слева, кнопки справа, всё по центру по высоте.
/* кнопки в шапке: логотип слева, действия справа, всё по центру */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
}justify-content на space-around или space-evenly в примере выше и смотри, как меняются зазоры между кнопками.4. Гибкость элементов
У flex-элементов три свойства «растяжимости». Вместе они отвечают на вопрос: как элемент ведёт себя, когда место есть или когда его не хватает.
flex-grow— как охотно элемент занимает лишнее место (0 — не растёт, 1 — растёт наравне со всеми);flex-shrink— как охотно элемент сжимается, когда места не хватает;flex-basis— стартовый размер до растягивания (px, % илиauto).
.card { flex: 1 1 0; }
/* равно: grow 1, shrink 1, basis 0.
Все карточки стартуют с нуля и делят место поровну. */Сокращение flex: 1 1 0 — самый частый вариант «равных карточек». С ним три карточки в ряду занимают одинаковую ширину независимо от содержимого: лишнее место распределяется одинаковыми долями.
.cards { display: flex; gap: 16px; }
.card {
flex: 1 1 0;
background: #f1f5f9;
padding: 20px;
border-radius: 10px;
}Порядок отображения меняется свойством order: по умолчанию все элементы имеют order: 0, элементы с меньшим значением идут раньше. Порядок в DOM не меняется — это не влияет на доступность и поисковую индексацию.
.main-news { order: -1; } /* выдвинуть на первое место */
.aside-news { order: 2; } /* опустить в конец */order для перестановки смысловых блоков, например чтобы главный заголовок визуально шёл раньше, чем в разметке. Скринридер и пользователь с тач-навигацией будут двигаться по порядку DOM, а не по картинке.5. Перенос и промежутки
По умолчанию flex-элементы стараются поместиться в одну строку. Когда элементов много, включай перенос: flex-wrap: wrap. Перенесённые строки дополнительно распределяются по поперечной оси свойством align-content.
.chips {
display: flex;
flex-wrap: wrap; /* элементы будут переноситься */
gap: 8px; /* зазор и внутри строки, и между ними */
align-content: flex-start;
}Свойство gap задаёт промежутки между элементами в обеих системах — flex и grid. Это большой плюс: не нужно обнулять маргины у крайних элементов и писать хак с отрицательными отступами.
/* теги-чипы: ряд с переносом и одинаковыми зазорами */
.chips span {
padding: 4px 12px;
border-radius: 999px;
background: #e2e8f0;
font-size: 14px;
}gap поддерживается во всех современных браузерах — смело используй его вместо margin на дочерних элементах. Исключение — очень старые браузеры, где придётся рисовать зазоры через классы .chips > * + * { margin-left: 8px }.6. Flexbox: практические приёмы
Идеальное центрирование
.holder {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}Так центрируется любой блок по обеим осям — три строки вместо табличной вёрстки и абсолютных позиций.
Липкий подвал через flex на body
html, body { min-height: 100vh; }
body {
display: flex;
flex-direction: column;
margin: 0;
}
.footer {
margin-top: auto; /* подвал прижимается к низу, даже если контента мало */
}Кнопка с иконкой
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
}
/* иконка и текст выстроятся в одну линию и не разъедутся */Счётчик в бейдже
/* «Уведомления» с круглым счётчиком в углу кнопки */
.badge {
position: relative;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
}
.badge-count {
position: absolute;
top: -6px; right: -6px;
min-width: 18px; height: 18px;
padding: 0 5px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999px;
background: #ef4444;
color: #fff;
font-size: 12px;
}display: inline-flex плюс gap — стандартный рецепт для кнопок со значками: элементы выстраиваются по вертикальному центру и между ними всегда одинаковый отступ.7. Grid: линии, колонки и ряды
Grid включается свойством display: grid, а дальше описываются колонки и ряды: grid-template-columns и grid-template-rows. Каждая колонка или ряд задаётся шириной или высотой соответствующей дорожки.
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px; /* три колонки */
grid-template-rows: 80px 1fr; /* шапка и контент */
gap: 16px;
}Единица fr (fraction) — это доля свободного места. Три колонки 1fr 2fr 1fr означают: одну часть — левой боковой, две части — контенту, одну — правой. Остальные длины (px, %, auto) фиксированы, и fr делит остаток.
| Единица | Поведение |
|---|---|
| px | жёсткий размер, не зависит от ширины контейнера |
| % / auto | адаптивный размер относительно контейнера |
| fr | доля свободного пространства после других колонок |
| minmax() | диапазон: от минимума до максимума |
/* длинная колонка содержимого и две боковые */
.page {
display: grid;
grid-template-columns: 240px 1fr 240px;
gap: 20px;
}gap в grid работает по обоим осям сразу: gap: 16px — и колонки, и ряды отступают друг от друга одинаково.8. Grid: repeat, minmax, автоматика
Писать десять одинаковых колонок вручную не нужно — есть функция repeat(). А для сеток с неизвестным числом элементов — два ключевых слова: auto-fill и auto-fit.
/* три равные колонки */
grid-template-columns: repeat(3, 1fr);
/* галерея: сколько влезет колонок от 200px, но не уже 1fr */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));auto-fill— браузер создаёт столько пустых треков, сколько помещается; колонки остаются зарезервированными;auto-fit— пустые треки схлопываются в ноль, и реальные колонки растягиваются на всю ширину. Для галерей обычно нужен именноauto-fit;minmax(200px, 1fr)— колонка не уже 200px, но готова растянуться на свободное место.
auto-fit на auto-fill — увидишь, как в конце ряда появятся «пустые» колонки, а карточки не растянутся на всю ширину.9. Grid: размещение по линиям и областям
Каждая колонка и ряд сетки ограничены линиями — они нумеруются с 1. Элемент можно разместить, указав начальную и конечную линию (направления grid-column-start/grid-column-end и аналогичные для рядов).
/* элемент занимает колонки с 1-й по 3-ю линию (две колонки) */
.banner {
grid-column-start: 1;
grid-column-end: 3;
}
/* то же самое одной строкой: */
.banner { grid-column: 1 / 3; }
/* растянуться на всю ширину: */
.banner { grid-column: 1 / -1; }Но самый читаемый способ собрать каркас страницы — именованные области через grid-template-areas. Каждая строка значения описывает один ряд сетки, каждое слово — область из колонок.
/* каркас страницы: header/sidebar/main/footer */
.page {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: 16px;
}
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }<div class="page">
<header class="page-header">Шапка</header>
<aside class="page-sidebar">Меню</aside>
<main class="page-main">Контент</main>
<footer class="page-footer">Подвал</footer>
</div>. в grid-template-areas означает пустую ячейку. Области должны образовывать прямоугольники без дырок — «буква Г» или две области по диагонали недопустимы, браузер проигнорирует объявление.10. Grid: выравнивание внутри ячеек
Когда элемент размещён в ячейке, можно управлять его положением внутри неё. Свойства justify-items (горизонталь) и align-items (вертикаль) на контейнере задают выравнивание для всех ячеек сразу, а justify-self и align-self — для отдельного элемента.
.board {
display: grid;
grid-template-columns: repeat(3, 120px);
grid-template-rows: repeat(2, 90px);
/* по умолчанию ячейка растягивает элемент — stretch */
justify-items: stretch;
align-items: stretch;
}
.pin {
align-self: center; /* только этот элемент — по центру по вертикали */
justify-self: center; /* и по центру по горизонтали */
}Классический приём — центрирование элемента по обеим осям внутри grid-ячейки:
.cell {
display: grid;
place-items: center; /* центр по горизонтали и вертикали */
min-height: 200px;
}place-items — сокращение сразу для align-items и justify-items. А в flexbox центрирование по обеим осям делается парой justify-content: center + align-items: center.11. Вложенность: grid-страница с flex-панелями
Теперь соберём всё вместе: полный макет страницы, где наружный каркас — Grid с именованными областями, а внутри контентной части — ряды карточек на flexbox. Это самая реальная структура сайта, который встречается в бою.
<div class="page">
<header class="page-header">Логотип и меню</header>
<aside class="page-sidebar">Категории</aside>
<main class="page-main">
<h2>Новости</h2>
<div class="cards">
<article class="card">Новость 1</article>
<article class="card">Новость 2</article>
<article class="card">Новость 3</article>
<article class="card">Новость 4</article>
</div>
</main>
<footer class="page-footer">Подвал</footer>
</div>.page {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: 16px;
}
.page-header { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-main { grid-area: main; }
.page-footer { grid-area: footer; }
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 240px; /* карточки равной ширины, не уже 240px */
padding: 16px;
border-radius: 10px;
background: #f1f5f9;
}display: flex на ячейке и flex-direction: column для вертикального ряда. Grid — каркас сверху, flex — панели внутри — это и есть типовой паттерн вёрстки.12. Адаптив без медиазапросов
Связка repeat(auto-fit, minmax(...)) решает большую часть адаптивных задач без единого медиазапроса: сколько колонок помещается при текущей ширине, столько и рисуется — на телефоне одна, на десктопе пять.
.products {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}Для шрифтов, не выпадающих из сетки, подходит функция clamp(): она задаёт минимум, желаемый размер и максимум. Текст плавно растёт вместе со страницей.
.title {
/* от 1.2rem до 2.5rem, в идеале 5vw — доля ширины экрана */
font-size: clamp(1.2rem, 5vw, 2.5rem);
}Когда медиазапросы всё же нужны:
- полная перестройка каркаса — например, двухколоночный макет превращается в одноколоночный;
- скрытие второстепенных блоков (меню → кнопка-«бургер»);
- изменение философии раскладки: карточки в ряд — в колонку.
@media (max-width: 720px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"footer";
}
.page-sidebar { display: none; } /* боковое меню прячем на телефоне */
}auto-fit + minmax держит целые сетки резиновыми сами по себе, а медиазапросы резервируются только для перестроения каркаса страницы.13. Частые ошибки
justify-* всегда работает вдоль главной оси (в flex) или по горизонтали (в grid), align-* — вдоль поперечной оси или по вертикали. Начинаешь менять одно место — ничего не происходит — значит, ты выравниваешь не ту ось.body могут конфликтовать со стилями браузера и плагинов. Для раскладки страницы выделяй обёртку-контейнер, а body оставляй нейтральным.gap делает это автоматически в flex и grid.height: 100px на содержимом мстит при длинном тексте: текст переполняет блок и лезет поверх соседнего. Задавай min-height вместо жёсткой высоты, а для отсечения лишнего используй перенос и обрезку осознанно.min-width: 0 на элемент, чтобы он мог сжиматься меньше содержимого.14. Чек-лист перед сдачей вёрстки
Пройдись по списку перед тем, как отдать макет в работу. Каждый пункт — отдельная проверка в DevTools и на живой странице.
- ряд элементов (кнопки, чипы, карточки) собран на flex, а не на float-обходах и inline-block;
- каркас страницы (шапка/сайдбар/контент/подвал) — на grid, с областями или линиями;
- нет горизонтального скролла: все колонки умещаются без «убегания» за край окна на ширине 360px и 1920px;
- зазоры через
gap, а не через маргины с расчётом «у первого нет»; - карточки равной ширины:
flex: 1 1 0илиrepeat(3, 1fr); - галереи и товарные сетки —
auto-fit+minmax, без сотен медиазапросов; - текст не обрывается на длинных словах — применён
min-width: 0иoverflow-wrap; - нет фиксированных высот у контентных блоков — только
min-height; - подвал прижат к низу даже на короткой странице;
- проверены обе системы на вкладке «Адаптив»: телефоны, планшеты, десктопы.
minmax и auto-fit.Что дальше
Ты освоил обе системы раскладки: flexbox для панелей и grid для страниц, гибкость элементов, области и адаптив. Закрепи материал в практике: собери собственный лендинг по Практике: лендинг. Дальше по курсу — движение, жизнь и реакция интерфейса в CSS-анимациях, а фундамент, на который всё это ложится, — в основном курсе CSS.