Пошук уроків, статей та іншого контенту
Застосуйте Flexbox для створення адаптивної навігації, карток і макета з колонками та рядками.
Flexbox зручно використовувати для макетів, у яких елементи розташовуються в одному основному напрямку: у рядку або колонці. На практиці за допомогою Flexbox можна створювати:
адаптивну навігацію;
сітки карток;
макети з бічною панеллю та основним вмістом;
рядки й колонки, що змінюють напрямок на малих екранах;
області, у яких окремі елементи займають доступний простір.
У цьому уроці поєднаємо ці сценарії в одному адаптивному макеті.
Щоб перетворити елемент на flex-контейнер, використовуйте:
.container {
display: flex;
}У контейнера є дві осі:
основна вісь — визначається властивістю flex-direction;
поперечна вісь — перпендикулярна до основної.
Значення flex-direction:
.container {
flex-direction: row; /* за замовчуванням */
}Основні значення:
row — елементи розташовані в рядок;
row-reverse — рядок у зворотному напрямку;
column — елементи розташовані в колонку;
column-reverse — колонка у зворотному напрямку.
Вирівнювання вздовж основної осі задає justify-content, а вздовж поперечної — align-items.
.container {
display: flex;
justify-content: space-between;
align-items: center;
}Для відстані між дочірніми елементами краще використовувати gap, а не додавати однакові зовнішні відступи кожному елементу:
.container {
display: flex;
gap: 1rem;
}Типова навігація складається з логотипа, списку посилань і кнопки дії. На широкому екрані ці елементи розміщуються в одному рядку. На вузькому — можуть переноситися або переходити в колонку.
<header class="site-header">
<a class="logo" href="/">DevSpace</a>
<nav class="main-nav" aria-label="Основна навігація">
<a href="#features">Можливості</a>
<a href="#courses">Курси</a>
<a href="#about">Про платформу</a>
</nav>
<a class="header-action" href="#start">Почати навчання</a>
</header>.site-header {
display: flex;
align-items: center;
gap: 2rem;
padding: 1rem 2rem;
}
.logo {
flex-shrink: 0;
}
.main-nav {
display: flex;
justify-content: center;
gap: 1.25rem;
flex: 1 1 auto;
}
.header-action {
flex-shrink: 0;
}Властивість flex є скороченням для трьох властивостей:
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
}У цьому прикладі:
flex: 1 1 auto дозволяє навігації займати доступний простір;
flex-shrink: 0 забороняє логотипу й кнопці стискатися;
навігація залишається центральною між двома крайніми елементами.
Якщо навігація має залишатися горизонтальною, але за недостатньої ширини переноситися на наступний рядок:
.site-header {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem 2rem;
}
.main-nav {
display: flex;
flex: 1 1 20rem;
flex-wrap: wrap;
justify-content: center;
gap: 0.75rem 1.25rem;
}flex-wrap: wrap дозволяє дочірнім елементам переходити на наступний рядок. Водночас gap зберігає однакові проміжки між рядками та колонками.
Для малого екрана можна змінити напрямок усієї навігації:
@media (max-width: 42rem) {
.site-header {
align-items: stretch;
flex-direction: column;
}
.main-nav {
justify-content: flex-start;
}
.header-action {
align-self: flex-start;
}
}Сітка карток є одним із найпоширеніших практичних сценаріїв Flexbox. Контейнер може розташовувати картки в рядок, а за недостатньої ширини — переносити їх.
.card-list {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card {
flex: 1 1 16rem;
}Значення flex: 1 1 16rem означає:
картка може збільшуватися;
картка може стискатися;
початковий розмір картки — 16rem;
якщо в рядку недостатньо місця, картка переноситься.
Це не означає, що картки завжди матимуть рівно 16rem. Flexbox розподіляє доступний простір між ними.
У flex-контейнері за замовчуванням використовується:
align-items: stretch;Тому елементи одного рядка можуть розтягуватися до висоти найвищої картки. Щоб вміст картки коректно розподілявся по вертикалі, саму картку також можна зробити flex-контейнером:
.card {
display: flex;
flex: 1 1 16rem;
flex-direction: column;
}
.card-content {
display: flex;
flex: 1;
flex-direction: column;
}
.card-action {
margin-top: auto;
}margin-top: auto займає весь доступний вільний простір і притискає кнопку до нижнього краю картки. Це допомагає вирівняти кнопки в картках із заголовками різної довжини.
Важливо, що margin-top: auto працює тут на основній вертикальній осі, оскільки .card-content має flex-direction: column.
Поширений макет складається з бічної панелі та основного вмісту:
.page-layout {
display: flex;
align-items: flex-start;
gap: 2rem;
}
.sidebar {
flex: 0 0 15rem;
}
.main-content {
min-width: 0;
flex: 1 1 auto;
}Розберімо значення:
flex: 0 0 15rem — бічна панель не збільшується і не стискається;
flex: 1 1 auto — основний вміст займає залишок ширини;
min-width: 0 — дозволяє flex-елементу стискатися менше за його вміст.
min-width: 0Flex-елементи мають автоматичний мінімальний розмір, який іноді визначається найдовшим словом, кодом або широким внутрішнім елементом. Через це основний вміст може не стискатися й створювати горизонтальне прокручування.
Для області, яка має займати залишок рядка, часто потрібне правило:
.main-content {
min-width: 0;
}Це особливо важливо для:
довгих URL;
блоків із кодом;
широких дочірніх елементів;
заголовків без пробілів.
На малих екранах бічну панель і основний вміст можна розташувати вертикально:
@media (max-width: 50rem) {
.page-layout {
flex-direction: column;
}
.sidebar {
flex-basis: auto;
width: 100%;
}
}Реальні макети зазвичай містять кілька незалежних flex-контейнерів. Наприклад:
сторінка розташовує бічну панель і вміст у рядок;
основний вміст розташовує заголовок і метадані в рядок;
список карток переносить картки на нові рядки;
кожна картка розташовує контент вертикально.
Налаштування одного контейнера не впливає безпосередньо на напрямок його батьківського або дочірнього контейнера.
.page-layout {
display: flex;
}
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-list {
display: flex;
flex-wrap: wrap;
}
.card {
display: flex;
flex-direction: column;
}Такий поділ відповідає структурі інтерфейсу: кожен контейнер відповідає за власну вісь і власне вирівнювання.
Нижче наведено завершений приклад сторінки з:
адаптивною навігацією;
макетом із бічною панеллю;
заголовком основного вмісту;
адаптивною сіткою карток;
однаковим вирівнюванням кнопок у картках.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Курси DevSpace</title>
<style>
:root {
color-scheme: light;
font-family: system-ui, sans-serif;
color: #172033;
background: #f4f6fb;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
a {
color: inherit;
}
.site-header {
display: flex;
align-items: center;
gap: 2rem;
padding: 1rem 2rem;
background: #172033;
color: #ffffff;
}
.logo {
flex-shrink: 0;
font-size: 1.25rem;
font-weight: 700;
text-decoration: none;
}
.main-nav {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
justify-content: center;
gap: 0.75rem 1.25rem;
}
.main-nav a,
.header-action {
text-decoration: none;
}
.main-nav a {
color: #d9e2f2;
}
.main-nav a:hover {
color: #ffffff;
}
.header-action {
flex-shrink: 0;
padding: 0.65rem 1rem;
border-radius: 0.5rem;
background: #6d5dfc;
color: #ffffff;
font-weight: 700;
}
.page-layout {
display: flex;
align-items: flex-start;
gap: 2rem;
max-width: 75rem;
margin: 0 auto;
padding: 2rem;
}
.sidebar {
flex: 0 0 15rem;
padding: 1.25rem;
border: 1px solid #dce2ef;
border-radius: 0.75rem;
background: #ffffff;
}
.sidebar h2 {
margin-top: 0;
font-size: 1.1rem;
}
.filters {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin: 0;
}
.filter {
display: flex;
align-items: center;
gap: 0.5rem;
}
.main-content {
min-width: 0;
flex: 1 1 auto;
}
.content-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1.5rem;
}
.content-header h1 {
margin: 0;
font-size: clamp(1.7rem, 4vw, 2.5rem);
}
.content-header p {
margin: 0.5rem 0 0;
color: #5f6b82;
}
.sort-control {
flex: 0 0 auto;
padding: 0.6rem;
border: 1px solid #c9d1e2;
border-radius: 0.5rem;
background: #ffffff;
}
.card-list {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.card {
display: flex;
flex: 1 1 16rem;
flex-direction: column;
min-width: 0;
padding: 1.25rem;
border: 1px solid #dce2ef;
border-radius: 0.75rem;
background: #ffffff;
}
.card-content {
display: flex;
flex: 1;
flex-direction: column;
}
.card h2 {
margin: 0;
font-size: 1.25rem;
}
.card p {
color: #5f6b82;
line-height: 1.5;
}
.card-meta {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
margin: 0.5rem 0;
color: #5f6b82;
font-size: 0.9rem;
}
.card-action {
align-self: flex-start;
margin-top: auto;
padding: 0.65rem 1rem;
border-radius: 0.5rem;
background: #172033;
color: #ffffff;
text-decoration: none;
}
@media (max-width: 50rem) {
.page-layout {
flex-direction: column;
}
.sidebar {
flex-basis: auto;
width: 100%;
}
.filters {
flex-direction: row;
flex-wrap: wrap;
}
}
@media (max-width: 42rem) {
.site-header {
align-items: stretch;
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
.main-nav {
justify-content: flex-start;
}
.header-action {
align-self: flex-start;
}
.page-layout {
padding: 1rem;
}
.content-header {
flex-direction: column;
}
.sort-control {
width: 100%;
}
}
</style>
</head>
<body>
<header class="site-header">
<a class="logo" href="/">DevSpace</a>
<nav class="main-nav" aria-label="Основна навігація">
<a href="#courses">Курси</a>
<a href="#tracks">Навчальні треки</a>
<a href="#about">Про платформу</a>
</nav>
<a class="header-action" href="#start">Почати навчання</a>
</header>
<div class="page-layout">
<aside class="sidebar">
<h2>Фільтри</h2>
<form class="filters">
<label class="filter">
<input type="checkbox" name="level" value="beginner">
Початковий рівень
</label>
<label class="filter">
<input type="checkbox" name="level" value="advanced">
Просунутий рівень
</label>
<label class="filter">
<input type="checkbox" name="format" value="practice">
Практичні курси
</label>
</form>
</aside>
<main class="main-content" id="courses">
<header class="content-header">
<div>
<h1>Курси для розробників</h1>
<p>Оберіть напрям і розвивайте практичні навички.</p>
</div>
<label>
<span class="visually-hidden">Сортування</span>
<select class="sort-control" name="sort">
<option>За популярністю</option>
<option>За складністю</option>
<option>За тривалістю</option>
</select>
</label>
</header>
<section class="card-list" aria-label="Список курсів">
<article class="card">
<div class="card-content">
<h2>HTML та CSS</h2>
<div class="card-meta">
<span>12 уроків</span>
<span>Просунутий</span>
</div>
<p>
Створення адаптивних інтерфейсів, компонентів і складних макетів.
</p>
<a class="card-action" href="#html-css">Переглянути курс</a>
</div>
</article>
<article class="card">
<div class="card-content">
<h2>JavaScript</h2>
<div class="card-meta">
<span>18 уроків</span>
<span>Середній</span>
</div>
<p>
Робота з даними, подіями та логікою сучасних вебзастосунків.
</p>
<a class="card-action" href="#javascript">Переглянути курс</a>
</div>
</article>
<article class="card">
<div class="card-content">
<h2>Доступність</h2>
<div class="card-meta">
<span>8 уроків</span>
<span>Середній</span>
</div>
<p>
Практики створення інтерфейсів, доступних для різних користувачів.
</p>
<a class="card-action" href="#accessibility">Переглянути курс</a>
</div>
</article>
</section>
</main>
</div>
</body>
</html>У прикладі клас visually-hidden використовується в HTML, але його стилі не додано. Якщо прихований текст потрібен для доступності, додайте:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}Flexbox може працювати не лише з одним рядом. За допомогою flex-wrap контейнер утворює кілька рядків:
.container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}Для контролю вирівнювання самих рядків використовується align-content. Вона має значення лише тоді, коли контейнер має кілька рядків і в ньому є вільний простір по поперечній осі.
.container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
gap: 1rem;
}Не плутайте:
align-items вирівнює елементи всередині кожного рядка;
align-content вирівнює всі рядки разом;
justify-content вирівнює елементи вздовж основної осі.
Наприклад, для контейнера з flex-direction: row:
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
align-content: flex-start;
}justify-content працює горизонтально;
align-items працює вертикально в межах рядка;
align-content розподіляє кілька рядків по вертикалі.
Властивість order змінює візуальний порядок flex-елементів:
.sidebar {
order: 1;
}
.main-content {
order: 0;
}За замовчуванням усі елементи мають order: 0. Елементи з меншим значенням відображаються раніше.
order може бути корисним для адаптивного макета, але його не слід застосовувати без потреби. Візуальний порядок може відрізнятися від порядку в HTML, що ускладнює навігацію клавіатурою та сприйняття сторінки допоміжними технологіями. Структуру HTML краще будувати в логічному порядку, а order використовувати лише для невеликих візуальних змін.
flex-wrap.card-list {
display: flex;
}Без flex-wrap: wrap картки намагатимуться залишитися в одному рядку й можуть стати надто вузькими або спричинити переповнення.
Правильніше:
.card-list {
display: flex;
flex-wrap: wrap;
}width замість flex-правил.card {
width: 33.333%;
}Такий підхід не враховує проміжки gap. Три картки шириною 33.333% плюс проміжки можуть не поміститися в один рядок.
Краще задати початковий розмір через flex-basis:
.card {
flex: 1 1 16rem;
}height: 100%Фіксоване або відсоткове значення висоти часто створює зайві порожні області чи переповнення. Для однакової висоти карток зазвичай достатньо:
.card-list {
display: flex;
align-items: stretch;
}
.card {
display: flex;
flex-direction: column;
}Причиною може бути довгий текст або flex-елемент із великим мінімальним розміром.
Для області основного вмісту:
.main-content {
min-width: 0;
}Для довгих слів можна також дозволити перенесення:
.card {
overflow-wrap: anywhere;
}Застосовуйте це правило лише там, де воно справді потрібне, оскільки воно може розривати слова в неочікуваних місцях.
justify-contentЯкщо контейнер має:
.container {
flex-direction: column;
}то justify-content вирівнює елементи вертикально, а не горизонтально. Для горизонтального вирівнювання в такому контейнері потрібна align-items.
orderЯкщо елемент має бути першим за змістом, краще розташувати його першим у HTML. CSS має змінювати презентацію, а не логіку документа.
display: flex створює flex-контейнер.
flex-direction визначає основну вісь: рядок або колонку.
justify-content працює вздовж основної осі.
align-items працює вздовж поперечної осі.
flex-wrap: wrap дозволяє елементам переходити на нові рядки.
gap задає проміжки між flex-елементами.
flex: 1 1 16rem є практичним шаблоном для адаптивних карток.
margin-top: auto притискає елемент до нижнього краю вертикальної картки.
min-width: 0 допомагає уникати переповнення в основному вмісті.
Вкладені flex-контейнери дають змогу незалежно налаштовувати навігацію, заголовки, картки та бічні панелі.
Медіазапити дозволяють змінювати напрямок і структуру макета на малих екранах.