CSS Flexbox: Вирівнювання та Позиціонування
CSS Flexbox: Вирівнювання та Позиціонування
Ми створили сітку. Як тепер нею керувати?
У попередній статті про основи Flexbox ми навчились створювати Flex-контейнер, керувати напрямком його Головної осі (Main Axis) та дозволяти елементам переноситись на нові рядки за допомогою flex-wrap.
Але справжня "магія" Flexbox, за яку його так полюбили розробники по всьому світу, криється в його інструментах вирівнювання.
Вам більше не потрібно вираховувати відсотки margins чи використовувати трюк absolute + transform: translate(-50%, -50%), щоб поставити блок рівно по центру екрану.
У Flexbox є чіткий набір властивостей, кожна з яких відповідає за певну вісь:
justify...— завжди працює з Головною віссю (Main Axis).align...— завжди працює з Поперечною віссю (Cross Axis).
Давайте розберемо кожну з них детально.
Вирівнювання по Головній осі: justify-content
Властивість justify-content застосовується до Flex-контейнера. Вона визначає, як браузер має розподілити вільний простір всередині контейнера вздовж Головної осі. Якщо Головна вісь горизонтальна (flex-direction: row), то це вирівняє елементи по горизонталі. Якщо вертикальна (column) — по вертикалі.
Уявіть, що ваші елементи — це книги на полиці. Якщо полиця ширша за суму товщин усіх книг, у вас залишається вільне місце. justify-content вирішує, куди саме "запхати" це вільне місце: зліва від книг, між ними, навколо них чи справа.
Доступні основні значення:
flex-start
center
flex-end
space-between
space-around
space-evenly
<div class="justify-controls">
<button onclick="setJustify('flex-start')" class="active">flex-start</button>
<button onclick="setJustify('center')">center</button>
<button onclick="setJustify('flex-end')">flex-end</button>
<button onclick="setJustify('space-between')">space-between</button>
<button onclick="setJustify('space-around')">space-around</button>
<button onclick="setJustify('space-evenly')">space-evenly</button>
</div>
<div class="jc-container" id="demo-jc">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
<script>
function setJustify(val) {
document.getElementById('demo-jc').style.justifyContent = val
document.querySelectorAll('.justify-controls button').forEach((b) => b.classList.remove('active'))
event.target.classList.add('active')
}
</script>
body {
font-family: system-ui, sans-serif;
}
.justify-controls {
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
button {
padding: 6px 14px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #f8fafc;
}
button.active {
background: #0ea5e9;
color: white;
border-color: #0284c7;
}
.jc-container {
display: flex;
/* Початкове значення */
justify-content: flex-start;
background-color: #f1f5f9;
padding: 15px;
border-radius: 8px;
border: 2px dashed #94a3b8;
min-height: 80px;
transition: all 0.3s ease;
}
.item {
background-color: #0ea5e9;
color: white;
padding: 15px 20px;
border-radius: 6px;
font-weight: 600;
}
Анатомія space-between проти space-evenly:
Часто розробники плутають ці два значення, хоча різниця очевидна:
space-between"приклеює" крайні елементи до стінок. Якщо вам треба зробити хедер, де логотип зліва, а кнопка входу — максимально справа,space-betweenвпорається з цим за 1 рядок коду.space-evenlyгарантує, що відстань перед першим елементом дорівнює відстані після останнього, і вона точнісінько така ж, як і відстані між самими елементами. Це гарно виглядає для галерей карток однакового розміру.
Вирівнювання по Поперечній осі: align-items
Якщо justify-content працює з тим напрямком, в якому елементи "течуть", то align-items відповідає за перпендикулярну вісь (Cross Axis).
Властивість застосовується також до Flex-контейнера, але діє на те, як елементи розташовуються всередині свого рядка по вертикалі (якщо flex-direction: row).
Значення цієї властивості вирішують знамениту проблему з "рівновисокими колонками" (коли фон бічної панелі мав закінчуватися там само, де текстовий контент, незалежно від наповнення).
<div class="align-controls">
<button onclick="setAlign('stretch')" class="active">stretch (default)</button>
<button onclick="setAlign('flex-start')">flex-start</button>
<button onclick="setAlign('center')">center</button>
<button onclick="setAlign('flex-end')">flex-end</button>
<button onclick="setAlign('baseline')">baseline</button>
</div>
<!-- Зверніть увагу: ми задали фіксовану висоту контейнеру -->
<div class="ai-container" id="demo-ai">
<div class="ai-item small">Малий</div>
<div class="ai-item big">Великий<br />блок<br />контенту</div>
<div class="ai-item medium">Середній<br />результат</div>
<!-- Блок спеціально для демо baseline -->
<div class="ai-item text-demo" style="font-size: 2rem; align-self: auto;">TEXT!</div>
</div>
<script>
function setAlign(val) {
document.getElementById('demo-ai').style.alignItems = val
document.querySelectorAll('.align-controls button').forEach((b) => b.classList.remove('active'))
event.target.classList.add('active')
}
</script>
body {
font-family: system-ui, sans-serif;
}
.align-controls {
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
button {
padding: 6px 14px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #f8fafc;
}
button.active {
background: #10b981;
color: white;
border-color: #059669;
}
.ai-container {
display: flex;
/* Контейнер має фіксовану висоту, щоб було куди вирівнювати */
height: 200px;
align-items: stretch; /* Початкове значення */
background-color: #f1f5f9;
padding: 15px;
border-radius: 8px;
border: 2px dashed #94a3b8;
gap: 10px;
transition: all 0.3s ease;
}
.ai-item {
background-color: #10b981;
color: white;
padding: 15px;
border-radius: 6px;
font-weight: 500;
}
/* Деякі блоки мають свій розмір (свій контент), а деякі - ні */
.small {
padding: 10px;
}
.medium {
padding: 20px;
}
.big {
padding: 30px;
font-weight: bold;
}
.text-demo {
background-color: #ef4444;
}
Анатомія коду та значень align-items:
stretch(За замовчуванням): Це те, що робить Flexbox магічним для побудови сіток. Елементи не мають своєї власної висоти (якщо явно не задано). Замість цього вони "розтягуються", щоб заповнити всю висоту Батьківського контейнера. Завдяки цьому картки в рядку завжди будуть однакової висоти!flex-start: Елементи приймають розмір свого вмісту (замість розтягування) і притуляються до верхнього краю контейнера (початку Поперечної осі).flex-end: Те ж саме, але елементи притуляються до нижнього краю. Всі "звисають" вниз.center: Елементи приймають розмір вмісту і центруються відносно висоти контейнера.baseline: Елементи вирівнюються так, щоб їхній текстовий вміст (конкретніше — перша "лінія" тексту всередині) розташовувався на одній невидимій базовій лінії. У нашому демо зверніть увагу на червоний блок з великим шрифтом: під часbaselineйого текст знаходиться на одному рівні зі словами "Малий", "Великий" в інших блоках, навіть якщо верхні або нижні краї самих блоків не збігаються! Це критично важливо для вирівнювання іконок поруч із текстом різного розміру.
Індивідуальний бунтар: align-self
Властивість align-items контролює усіх дітей контейнера масово. Але що, якщо нам потрібно, щоб 99% елементів були по центру, а лише один конкретний притиснувся до низу?
Для цього існує властивість align-self, яка застосовується безпосередньо до Flex-елемента (дитини). Вона перевизначає те, що батько сказав через align-items для цього конкретного елемента.
<div class="self-demo">
<div class="item">Я слухаю батька (center)</div>
<!-- Цей елемент порушує правила -->
<div class="item self-start">Я сам по собі (flex-start)</div>
<div class="item">Я слухаю батька (center)</div>
<div class="item self-stretch">Я розтягуюсь (stretch)</div>
</div>
.self-demo {
display: flex;
/* Батько каже: "Всі по центру!" */
align-items: center;
height: 180px;
background-color: #f1f5f9;
padding: 15px;
border-radius: 8px;
gap: 10px;
}
.item {
background-color: #6366f1;
color: white;
padding: 15px;
border-radius: 6px;
font-weight: 500;
}
/* Індивідуальні стилі для конкретних дітей */
.self-start {
align-self: flex-start;
background-color: #ef4444; /* Червоний для бунтаря */
}
.self-stretch {
align-self: stretch;
background-color: #f59e0b; /* Жовтий */
}
justify-self. Ви не можете змусити один елемент вишиковуватися певним чином по горизонталі, використовуючи магічне слово justify-self (ця властивість працює лише в CSS Grid).
Якщо вам у flex-контейнері треба один елемент відсунути вліво, а всі інші вправо — ви використовуєте автоматичні відступи margin-left: auto; на потрібному елементі.Священний Грааль Вебдизайну: Ідеальне Центрування (Perfect Centering)
Знаючи те, як працюють осі та вирівнювання, ми зараз розкриємо найбільший секрет Flexbox, який змінив життя розробників на "до" і "після".
Раніше на співбесідах фронтенд-девелопери годинами креслили на дошках методи відцентровування блоку по горизонталі та вертикалі в центрі екрана. Сьогодні правильна відповідь складається з трьох рядків коду на батьківському контейнері:
.hero-section {
display: flex; /* 1. Вмикаємо розумний макет */
justify-content: center; /* 2. Центруємо по горизонталі (Головна вісь) */
align-items: center; /* 3. Центруємо по вертикалі (Поперечна вісь) */
}
<div class="perfect-centering-wrapper">
<div class="login-card">
<h3>Вхід в систему</h3>
<p>Я ідеально відцентрований з усіх боків, незалежно від розміру дисплея!</p>
<button>Клас!</button>
</div>
</div>
body {
font-family: system-ui, sans-serif;
margin: 0;
}
.perfect-centering-wrapper {
/* Робимо висоту, щоб було де центрувати */
height: 350px;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
border-radius: 8px;
/* ТРИ РЯДКИ МАГІЇ */
display: flex;
justify-content: center;
align-items: center;
}
.login-card {
background-color: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
text-align: center;
max-width: 300px;
}
h3 {
margin-top: 0;
color: #1e293b;
}
p {
color: #475569;
font-size: 14px;
line-height: 1.5;
}
button {
background-color: #3b82f6;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
}
Це працюватиме ідеально, навіть якщо всередині .login-card раптово додасться більше тексту і він стане значно вищим, або якщо на мобільному пристрої зміниться ширина екрану. Flexbox виконує важку математику за нас.
Вирівнювання багаторядкового контенту: align-content
Чи пам'ятаєте ви властивість flex-wrap: wrap з попередньої статті? Вона дозволяла елементам переноситись на нові лінії.
Коли елементи дійсно перенеслися і створили багато ліній (рядків) всередині Flex-контейнера, виникає потреба вирівнювати не самі елементи всередині їхньої лінії, а цілі лінії (рядки) контенту відносно загальної висоти контейнера!
Для цього слугує властивість align-content. Можна вважати її аналогом justify-content, але для рядків по Поперечній осі.
Значення ті ж самі:
flex-start: Всі рядки збираються докупи у верхній частині контейнера.flex-end: Всі рядки притискаються до низу.center: Всі рядки кучкуються по центру.space-between/space-around: Рядки розділяються рівними великими проміжками.stretch(За замовчуванням): Рядки розтягуються, щоб заповнити весь простір контейнера (їхня власна висота збільшується).
<div class="content-controls">
<button onclick="setContent('stretch')" class="active">stretch (default)</button>
<button onclick="setContent('flex-start')">flex-start</button>
<button onclick="setContent('center')">center</button>
<button onclick="setContent('space-between')">space-between</button>
</div>
<div class="ac-container" id="demo-ac">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
<script>
function setContent(val) {
document.getElementById('demo-ac').style.alignContent = val
document.querySelectorAll('.content-controls button').forEach((b) => b.classList.remove('active'))
event.target.classList.add('active')
}
</script>
body {
font-family: system-ui, sans-serif;
}
.content-controls {
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
button {
padding: 6px 14px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #f8fafc;
}
button.active {
background: #db2777;
color: white;
border-color: #be185d;
}
.ac-container {
display: flex;
flex-wrap: wrap; /* ОБОВ'ЯЗКОВО для роботи align-content! */
align-content: stretch; /* Початкове значення */
height: 300px; /* Великий контейнер, щоб рядкам було куди рухатись */
width: 250px; /* Вузький контейнер, щоб спровокувати переноси на нові рядки */
background-color: #fce7f3;
padding: 10px;
border-radius: 8px;
border: 2px dashed #f472b6;
gap: 10px; /* Проміжки між елементами в самому ряді */
transition: all 0.3s ease;
}
.item {
background-color: #db2777;
color: white;
/* Фіксована ширина/висота для наочності */
width: 60px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
font-weight: bold;
}
Ключове правило align-content:
Ця властивість працює виключно, якщо виконані ДВІ умови:
- Ви маєте
flex-wrap: wrapабоwrap-reverse. - Контейнер достатньо високий (Cross Axis довша, ніж сума висот всіх рядків контенту).
Якщо елементи вмістилися всі в один рядок (nowrap), тоді align-content взагалі не матиме абсолютно ніякого ефекту, тому що лінії "тільки одна". Ви маєте користуватися лише align-items.
Резюме та Практика
Вітаємо, тепер ви знаєте про вирівнювання у сітках практично все, що треба:
justify-content— вирівнює елементи вздовж Головної осі (зазвичай зліва направо, якщо це класичнийrow).align-items— вирівнює елементи вздовж Поперечної осі (зазвичай зверху вниз). Відповідає за те, розтягуються блоки чи ні.align-self— дозволяє одному елементу "не слухатись"align-itemsбатька.align-content— вирівнює цілі рядки в багаторядковому макеті відносно Поперечної осі (коли в нас є іflex-wrap: wrap, і запасне вільне місце).
Практичні Навички рівня "Логіка"
Сценарій: Ви робите "шапку" сайту (header). У ній є три елементи:
- Логотип
- Центральне меню навігації (головна, про нас)
- Права колонка: Рядок пошуку.
Ви хочете, щоб логотип був повністю притиснутий до лівого краю. Пошукова стрічка — цілком до правого краю. А меню опинилось рівно по геометричному центру вікна браузера.
Як це зробити?
- Встановіть
display: flexна header. - Встановіть
justify-content: space-between. Оскільки у вас рівно 3 елементи (логотип,<nav>, і блок пошуку)space-betweenзалишить перший на початку, третій в кінці, а другий залишить з рівною відстанню від стінок, гарантуючи йому місце по центру! Важливо: щоб меню було фізично по центру, блоки 1 та 3 повинні мати однакову ширину. ::Сценарій: Внизу у вашому футері є контейнер для іконок соціальних мереж:
<div> <a>FB</a> <a>TW</a> <a>IN</a> </div>. Ви хочете, щоб ці іконки розділились красивим рівномірним відступом як на початку, так і всередині і в кінці (ніби вони симетрично розкидані по центру вільної зони).Як це зробити? На батьківський контейнер потрібно навісити:
display: flex; justify-content: space-evenly;
Ніколи не хардкодьте проміжків на кшталт
margin-left: 25pxу таких сценаріях. Якщо ви додасте четверту соціальну мережу,space-evenlyперерахує математику автоматично, а з хардкодом дизайн поїде.
Тепер ми контролюємо сітку та переміщуємо елементи в будь-яку точку. Але що, якщо один блок нам потрібен ширший за інший? Як зробити так, щоб блок тексту розтягнувся і зайняв 100% простору, який залишився поруч із фіксованим аватаром?
CSS Flexbox: Гнучкість Розмірів та Практичні Патерни
Як поділити простір чесно?
Відцентровувати об'єкти і створювати проміжки між ними — це чудово. Ми розібрали це у статтях про Основи та Вирівнювання.
Але що робить Flexbox "гнучким" (flexible)? Його здатність обчислювати динамічні розміри.
Уявіть, що у вас є 1000 пікселів вільного місця і 3 блоки.
- Як зробити так, щоб кожен блок отримав рівно по третині простору, незалежно від обсягу тексту в ньому?
- Як сказати браузеру: "Цей блок має зайняти весь вільний простір, який залишився після того, як бічне меню візьме свої фіксовані 250 пікселів"?
Саме тут на сцену виходять властивості Flex-елементів. До цього ми працювали переважно з Контейнером. Тепер керуватимемо поведінкою самих блоків усередині нього.
Три Стовпи Гнучкості: grow, shrink, basis
Поведінка будь-якого елемента у flex-контейнері визначається трьома властивостями. Запам'ятайте їхню хронологію:
- Спочатку елемент має свій початковий ідеальний розмір (
flex-basis). - Потім, якщо в контейнері залишився ВІЛЬНИЙ простір, елемент може розширитись, щоб поглинути його (
flex-grow). - Якщо навпаки, простору НЕ ВИСТАЧАЄ, елемент може стиснутись, щоб влізти (
flex-shrink).
Розглянемо ці фази детально.
1. flex-basis — Початковий розмір
Перш ніж браузер почне рахувати вільне місце, він питає кожен елемент: "Який розмір тобі потрібен?". Властивість flex-basis визначає ідеальний, початковий розмір елемента уздовж Головної осі.
Якщо flex-direction: row — це вісь X (аналог ширини). Якщо flex-direction: column — це вісь Y (аналог висоти).
.item-1 {
flex-basis: 250px;
} /* Я хочу 250px */
.item-2 {
flex-basis: 30%;
} /* Я хочу 30% від контейнера */
.item-3 {
flex-basis: auto;
} /* Довжина мого контенту (за замовчуванням) */
.item-4 {
flex-basis: 0;
} /* Я не маю початкового розміру, хочу нуль! */
width та flex-basis:flex-basis впливає на ту вісь, яка зараз є "Головною". При column він встановлює висоту. width — це жорстке фізичне обмеження. Зазвичай рекомендується використовувати flex-basis у flex-контейнерах, оскільки це семантично пов'язане із механікою визначення вільного місця.2. flex-grow — Здатність зростати (Жадібність)
Після того, як браузер роздав усім елементам їхній flex-basis, у контейнері може залишитись 500 пікселів вільного місця. Кому його віддати? За це відповідає flex-grow.
Він приймає числове значення без одиниць виміру (як пропорцію).
Значення за замовчуванням — 0 (не забирати зайве місце).
<p class="demo-label">flex-grow: 0 (За замовчуванням: місце залишається пустим)</p>
<div class="fg-demo">
<div class="fg-item">Блок 1</div>
<div class="fg-item">Блок 2 (великий текст)</div>
<div class="fg-item">Блок 3</div>
</div>
<p class="demo-label">Всі елементи мають flex-grow: 1 (Ділимо вільне порівну!)</p>
<div class="fg-demo">
<div class="fg-item" style="flex-grow: 1;">Блок 1</div>
<div class="fg-item" style="flex-grow: 1;">Блок 2 (великий текст)</div>
<div class="fg-item" style="flex-grow: 1;">Блок 3</div>
</div>
<p class="demo-label">1 : 2 : 1 (Другий блок поглинає в 2 рази більше зайвого!)</p>
<div class="fg-demo">
<div class="fg-item" style="flex-grow: 1;">Блок 1</div>
<div class="fg-item fg-highlight" style="flex-grow: 2;">Я забираю x2</div>
<div class="fg-item" style="flex-grow: 1;">Блок 3</div>
</div>
body {
font-family: system-ui, sans-serif;
font-size: 14px;
}
.demo-label {
font-weight: 600;
color: #475569;
margin: 15px 0 5px;
}
.fg-demo {
display: flex;
background-color: #f1f5f9;
padding: 10px;
border-radius: 8px;
gap: 10px;
border: 2px dashed #94a3b8;
}
.fg-item {
background-color: #8b5cf6;
color: white;
padding: 10px 15px;
border-radius: 6px;
font-weight: 500;
}
.fg-highlight {
background-color: #f59e0b; /* Жовтий */
}
Анатомія другого рядка демо:
Елементи мали свій контент (flex-basis: auto). Перший взяв на себе умовні 50px ширини тексту, другий — 150px, третій — 60px. Залишилось 300px порожнечі. Оскільки кожен має flex-grow: 1 (сума "долей" = 3), браузер видав кожному по 100px зверху.
Результат: 2-й елемент все одно ширший за інші, бо він був великим початково! Браузер поділив ПРИРІСТ, а не зрівняв їхні розміри в кінці. (Щоб зрівняти, нам потрібен flex-basis: 0 — про це нижче).
3. flex-shrink — Здатність стискатися
Ця властивість діє, коли розмір елементів сумарно більший за контейнер (і при цьому flex-wrap: nowrap, інакше вони б просто перенеслися на новий рядок).
Браузер питає: "Так, у нас дефіцит 200px. З кого спишемо?".
Значення за замовчуванням — 1 (Всі стискаються однаково).
0 означає, що елемент "закріплений" і відмовляється стискатись.
.sidebar {
width: 250px;
flex-shrink: 0; /* Не стискай мій сайдбар за жодних обставин! */
}
.article {
width: 800px;
flex-shrink: 1; /* Звужуй текст, якщо користувач з планшета */
}
<div class="shrink-container">
<div class="s-item fixed">flex-shrink: 0 (Я не стискаюсь)</div>
<div class="s-item adaptive">flex-shrink: 1 (Ми стискаємось)</div>
<div class="s-item adaptive">flex-shrink: 1 (Ми стискаємось)</div>
</div>
<p class="h-text">💡 Контейнер має ширину 400px, а сумарний розмір елементів — 600px. Червоний блок зберіг свої 200px, а сині — поділили дефіцит між собою.</p>
body { font-family: system-ui, sans-serif; }
.shrink-container {
display: flex;
width: 400px;
background: #f1f5f9;
padding: 10px;
gap: 10px;
border: 2px dashed #94a3b8;
border-radius: 8px;
}
.s-item {
flex-basis: 200px;
padding: 15px;
color: white;
font-size: 13px;
font-weight: 600;
text-align: center;
border-radius: 6px;
}
.fixed {
background-color: #ef4444;
flex-shrink: 0;
}
.adaptive {
background-color: #3b82f6;
flex-shrink: 1;
}
.h-text {
font-size: 12px;
color: #64748b;
margin-top: 8px;
}
Стандарт Індустрії: Шорткат flex
Писати три властивості окремо (grow, shrink, basis) — довго, і це часто призводить до помилок (наприклад, забутий flex-shrink: 0 на іконці розтягує її в овал).
У 99% випадків світова розробка використовує шорткат flex.
Синтаксис:
/* flex: <flex-grow> <flex-shrink> <flex-basis> */
.item {
flex: 0 1 auto; /* Це магія за замовчуванням */
}
Давайте запам'ятаємо 4 класичних патерни:
1. flex: 1
flex: 1 (аналог 1 1 0%).
Поведінка: "Ігноруй мій контент, зроби нас усіх рівними!".
Браузер каже: ваш початковий розмір 0 (basis). Тепер ділимо весь простір за flex-grow. Три елементи отримають по 33.33%, скільки б тексту в них не було.2. flex: auto
flex: auto (аналог 1 1 auto).
Поведінка: "Рости і стискайся, але з повагою до мого розміру тексту".
Схожий на flex: 1, але тут є база auto. Блок з довшим текстом завжди буде більшим і займе більшу частину залишкового простору.3. flex: none
flex: none (аналог 0 0 auto).
Поведінка: "Мій розмір фіксований!".
Ідеально підходить для аватарів, іконок або блоку логотипу в навігаційному меню. Він ніколи не розтягнеться і ніколи не звузиться, навіть якщо екран дуже маленький (він краще вийде за межі контейнера, ніж стиснеться).4. flex: 0 0 250px
width: 250px.В ідеальному світі...
<p class="lbl">Погляньте на різницю між "flex: 1" та "flex: auto"</p>
<div class="comparison-container">
<div class="row">
<span>flex: 1 (Усі 33.33%) →</span>
<div class="box" style="flex: 1">Коротко</div>
<div class="box" style="flex: 1">Дуууууужжжжжеее довгий текст</div>
<div class="box" style="flex: 1">Варто.</div>
</div>
<div class="row">
<span>flex: auto (Повага до тексту) →</span>
<div class="box" style="flex: auto">Коротко</div>
<div class="box" style="flex: auto">Дуууууужжжжжеее довгий текст</div>
<div class="box" style="flex: auto">Варто.</div>
</div>
</div>
body {
font-family: system-ui, sans-serif;
font-size: 13px;
margin: 0;
}
.lbl {
font-weight: 600;
color: #475569;
}
.comparison-container {
background: #f1f5f9;
padding: 10px;
border-radius: 6px;
}
.row {
display: flex;
gap: 10px;
align-items: stretch;
margin-bottom: 10px;
}
span {
flex: 0 0 150px;
font-weight: 600;
padding: 10px 0;
}
.box {
background-color: #f43f5e;
color: white;
padding: 10px;
border-radius: 4px;
font-weight: 500;
}
Зміна Візуального Порядку: Властивість order
Одна з найпотужніших концепцій Flexbox полягає в тому, що він розділяє логічну розмітку (HTML) від візуальної παρουсiї (CSS).
За замовчуванням всі Flex-елементи мають order: 0. Браузер малює їх в тому порядку, у якому вони записані в HTML.
Але якщо ми додамо одному з елементів позитивне або негативне число order, браузер намалює їх відповідно до цієї математичної послідовності. Від найменшого до найбільшого.
<div class="order-demo">
<div class="o-item" style="order: 3;">
1 в HTML <br />
(став 3-м)
</div>
<div class="o-item" style="order: 1;">
2 в HTML <br />
(став 1-м)
</div>
<div class="o-item" style="order: 4;">
3 в HTML <br />
(став останнім)
</div>
<div class="o-item" style="order: 2;">
4 в HTML <br />
(став 2-м)
</div>
</div>
body {
font-family: system-ui, sans-serif;
font-size: 14px;
}
.order-demo {
display: flex;
gap: 10px;
background-color: #f1f5f9;
padding: 15px;
border-radius: 8px;
text-align: center;
}
.o-item {
flex: 1;
background-color: #0ea5e9;
color: white;
padding: 20px 10px;
border-radius: 6px;
font-weight: 600;
}
Коли це рятує життя?
Уявіть мобільне меню адаптивного сайту. На комп'ютері: Логотип - Навігація - Вхід. На мобільному телефоні кнопка входу може зміститися під меню-бургер. За допомогою медіа-запитів (@media) ви просто змінюєте значення order для мобільних екранів і блоки переміщуються самі собою:
@media (max-width: 600px) {
.nav-links {
order: 3; /* Спустили меню в низ */
width: 100%; /* Розтягнули на 100% */
}
}
order або flex-direction: *-reverse розриває візуальний та логічний порядок.
Коли людина використовує клавіатуру () для навігації сайтом, фокус переміщуватиметься на основі оригінальногo DOM (нашого HTML-коду!), а не того, що ви намалювали, змінивши order. На екрані кнопка може здаватись другою, а фокус полетить через всі елементи кудись вниз.
Змінюйте order тільки для декоративних цілей. Якщо послідовність важлива, перепишіть ваш HTML.Збираємо Все Разом: Практичні Патерни
Тепер ми знаємо як створити сітку, як її вирівняти, і як задати елементам гнучкий розмір. З цього набору інструментів можна побудувати 95% архітектури будь-якого сучасного сайту.
Розглянемо найпопулярніші макети реального світу.
Патерн 1: Компонент "Медіа Об'єкт"
Класичний блок "Аватарка автора і текст відгуку". Текст має розтягнутись і зайняти весь вільний правий простір, а аватарка має бути фіксованою, не стискатись, якщо тексту багато, але також не розтягуватись у висоту.
.comment {
display: flex; /* Рядок за замовчуванням */
gap: 1rem; /* Відступ між аватаром і текстом */
align-items: flex-start; /* Щоб обидва блоки притулились до верху, інакше stretch розтягне аву */
}
.avatar {
flex: none; /* Аналог 0 0 auto. Аватар ніколи не сплюснеться! */
width: 64px;
height: 64px;
border-radius: 50%;
}
.content {
flex: 1; /* Рости і заповнюй весь простір контейнера */
}
<div class="comment">
<img src="https://ui-avatars.com/api/?name=Alex+Doe&background=8b5cf6&color=fff" class="avatar" alt="User">
<div class="content">
<h4 style="margin: 0 0 5px 0;">Alex Doe</h4>
<p style="margin: 0; color: #475569; font-size: 14px;">Цей Flexbox неймовірний! Раніше я витрачав години на вирівнювання аватарок, а тепер це займає лічені секунди. Дивіться, як текст займає все вільне місце, а картинка залишається ідеально круглою.</p>
</div>
</div>
body { font-family: system-ui, sans-serif; }
.comment {
display: flex;
gap: 1rem;
align-items: flex-start;
background: #f8fafc;
padding: 20px;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.avatar {
flex: none;
width: 64px;
height: 64px;
border-radius: 50%;
object-fit: cover;
}
.content {
flex: 1;
}
Патерн 2: Навігаційна панель із "розривом"
Рядок із лого зліва, меню по центру і профілем юзера справа.
.navbar {
display: flex;
align-items: center; /* Центруємо всі елементи по вертикалі */
}
.logo {
margin-right: auto; /* Хак: "відштовхни" все, що праворуч від тебе, настільки, наскільки це можливо! */
}
.nav-links {
/* Не задаємо марджінів, він стоятиме рівно там, де його притисне сусід */
}
.profile {
margin-left: auto; /* Відштовхни все зліва! */
}
<nav class="navbar">
<div class="logo">KOSTYL.DEV</div>
<div class="nav-links">
<a href="#">Курси</a>
<a href="#">Статті</a>
</div>
<div class="profile">Увійти</div>
</nav>
<p class="h-text">💡 `margin-right: auto` на логотипі відштовхнув меню вправо, а `margin-left: auto` на профілі відштовхнув його від меню. Це створює ідеальні "розриви".</p>
body { font-family: system-ui, sans-serif; }
.navbar {
display: flex;
align-items: center;
background: #1e293b;
padding: 10px 20px;
border-radius: 8px;
color: white;
}
.logo {
font-weight: 800;
font-size: 1.2rem;
color: #3b82f6;
margin-right: auto;
}
.nav-links {
display: flex;
gap: 15px;
}
.nav-links a {
color: #cbd5e1;
text-decoration: none;
font-size: 14px;
}
.profile {
margin-left: auto;
background: #3b82f6;
padding: 5px 15px;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.h-text {
font-size: 12px;
color: #64748b;
margin-top: 8px;
}
Патерн 3: Sticky Footer ("Липкий" Підвал сторінки)
Проблема, якою згадують веб-розробку 90-х: якщо на сторінці мало контенту (2-3 рядки), підвал ("копірайт 2024") теліпається посеред білого екрану, а під ним — пустота вікна браузера. Як зробити так, щоб він завжди був притиснутим до низу? Flexbox вирішує це миттєво.
Побудова сторінки:
<body class="site-wrapper">
<header>Шапка</header>
<main class="content">Тут всього 2 рядки тексту</main>
<footer>Підвал</footer>
</body>
.site-wrapper {
display: flex;
flex-direction: column; /* Вертикальна гоовна вісь */
min-height: 100vh; /* Контейнер ОБОВ'ЯЗКОВО мінімум 100% висоти екрану! */
}
.content {
flex: 1; /* Основний контент повинен вирости (flex-grow: 1), поглинаючи ВЕСЬ пустий простір! */
}
/* footer і header можуть не мати flex взагалі, вони просто візьмуть стільки висоти, скільки в них тексту */
/* .content розштовхає їх по краях! */
<div class="site-wrapper">
<header>HEADER (Fixed Height)</header>
<main class="content">
<h3>Main Content Area</h3>
<p>На цій сторінці дуже мало контенту. Але завдяки `flex: 1` цей блок розтягнувся на всю доступну висоту вікна, надійно "притиснувши" підвал до самого низу екрана.</p>
</main>
<footer>FOOTER (Sticky)</footer>
</div>
body { margin: 0; font-family: system-ui, sans-serif; }
.site-wrapper {
display: flex;
flex-direction: column;
height: 400px; /* Для демо обмежимо висоту, в реальності мін-height: 100vh */
border: 2px solid #e2e8f0;
border-radius: 8px;
}
header {
background: #f1f5f9;
padding: 15px;
text-align: center;
font-weight: bold;
border-bottom: 1px solid #e2e8f0;
}
.content {
flex: 1;
padding: 20px;
background: white;
}
footer {
background: #1e293b;
color: white;
padding: 15px;
text-align: center;
}
Патерн 4: Макет "Святий Грааль" (Holy Grail Layout)
Структура типу:
[Header]
[Nav (ліворуч)] [Основний текст] [Садбар (праворуч)]
[Footer]
Раніше вимагало жахливих табличних костилів.
/* Перетворюємо саму сторінку, як у Sticky Footer */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
header,
footer {
/* Автоматично отримують висоту контенту і займають ширину 100% */
}
/* Середній блок огортає всі 3 колонки */
.main-wrapper {
display: flex; /* Тепер тут горизонтальна вісь (row)! */
flex: 1; /* Ця обгортка має розтягнутись (Sticky footer) */
}
.nav-left {
flex: 0 0 100px; /* Фіксовано */
order: -1; /* Візуально гарантуємо, що він зліва (навіть якщо в HTML він нижче основи) */
}
.main-content {
flex: 1; /* Змістовна середина займає все залишкове місце */
}
.sidebar-right {
flex: 0 0 120px; /* Фіксовано */
}
<div class="holy-grail">
<header>HEADER</header>
<div class="main-wrapper">
<main class="main-content">MAIN CONTENT (Flex: 1)</main>
<nav class="nav-left">NAV (Order: -1)</nav>
<aside class="sidebar-right">SIDEBAR</aside>
</div>
<footer>FOOTER</footer>
</div>
body { margin: 0; font-family: system-ui, sans-serif; font-size: 12px; }
.holy-grail {
display: flex;
flex-direction: column;
height: 350px; /* Для демо */
}
header, footer {
background: #cbd5e1;
padding: 10px;
text-align: center;
font-weight: bold;
}
.main-wrapper {
display: flex;
flex: 1;
}
.main-content {
flex: 1;
background: white;
padding: 15px;
display: flex;
align-items: center;
justify-content: center;
border-left: 1px solid #e2e8f0;
border-right: 1px solid #e2e8f0;
}
.nav-left {
flex: 0 0 80px;
background: #f1f5f9;
order: -1;
display: flex;
align-items: center;
justify-content: center;
}
.sidebar-right {
flex: 0 0 80px;
background: #f1f5f9;
display: flex;
align-items: center;
justify-content: center;
}
Як бачимо, флексбокс вкладається у флексбокс: body є вертикальним контейнером, а .main-wrapper є горизонтальним контейнером.
Резюме та Практика
Вітаємо, ви оволоділи Flexbox. Три ключові концепції, які ми розібрали для елементів:
flex-growдозволяє елементам розростатися і поглинати залишковий "зайвий" простір.flex-shrinkдозволяє елементам зжиматися у тісноті.flex-basisвизначає ідеальну теоретичну "вагу" (розмір) цього елемента до того, як розпочнуться математичні баталії за простір.
І найголовніше — ми зводимо це до зручних скорочень: flex: 1, flex: auto, flex: none.
Закріпимо наші знання на реальних задачах!
Practice Time: Рівень 1 (Базовий/Виправлення)
Пролема: У вас є <div class="navbar"> з display: flex. Всередині 10 пунктів меню і картинка логотипу (200x50 пікселів). Екран стає завузьким, і логотип стискається до гидких 50px ширини, спотворюючи пропорції.
Виправлення: Яке одне правило додати в клас .logo, щоб розв'язати проблему?
Рішення
Додайте `flex-shrink: 0;` до `.logo`, або використайте шорткат `flex: none;`. Це заборонить браузеру зжимати цей компонент під час дефіциту простору в контейнері.Проблема: Ваш header є flex-контейнером. Зліва є logo, по центру search, справа avatar.
Лого і Аватар мають бути фіксовані по 50px. Пошук має розтягнутися і зайняти все інше вільне місце посередині.
Завдання: Напишіть властивість flex для search та для бічних блоків.
Рішення
Для `logo` та `avatar`: `flex: none;` (або `flex: 0 0 50px`). Для `search`: `flex: 1;`. Він поглине весь доступний залишок після вирахування 100px на обидва іконки.Practice Time: Рівень 2 (Логіка/Колекції)
Сценарій: Ви маєте список з 12 карток блогу в <section class="posts">. Ви хочете, щоб на широкому екрані було 3 картки в ряд, а проміжки дорівнювали 20px.
Завдання: Складіть CSS для .posts та для карток .card, щоб це запрацювало коректно, незалежно від розміру екрану.
Ключ до рішення
Для контейнера `.posts`: ```css display: flex; flex-wrap: wrap; /* Дозволяє карткам утворювати багаторядковість! */ gap: 20px; ``` Для `.card`: нам потрібно поділити ширину (100% мінус два відступи по 20px (тобто 40px)) на 3. Можна використати `calc()` у властивості `flex-basis`: ```css /* Ми вираховуємо "третьову частину", мінус проміжки */ flex: 1 1 calc((100% / 3) - 20px); ``` Цей код дозволить карткам ідеально вишиковуватися і розтягуватись, займаючи простір у рівних частках.Practice Time: Рівень 3 (Архітектура/Створення)
Фінальний Бос: Створіть ідеальну картку товару в інтернет-магазині <article class="product">, використовуючи тільки Flexbox:
- Вона повинна мати чіткий кордон, тінь (для краси).
- Всередині йдуть зверху вниз: картинка, заголовок, опис, блок з ціною і кнопка.
- В галереї всі картки мають однакову висоту (бо це
flexу галереї). Але тексти в товарах різні за обсягом. - Обов'язкова умова: Блок із Ціною і Кнопкою повинен ЗАВЖДИ бути притиснутий до самого низу картки товару, незалежно від того, де закінчився опис!
Як це реалізувати архітектурно:
Сама картка товару `.product` стає флекс-контейнером!.product {
display: flex;
flex-direction: column; /* Зверху вниз */
/* ... інші візуальні стилі, тінь тощо ... */
}
Всередині у нас кілька блоків (картинка, текст, блок кнопки). Щоб відкинути блок із кнопкою в самий низ, ми перетворюємо його на "пружину":
.button-group {
margin-top: auto;
/* Цей геніальний трюк поглинає весь вільний вертикальний
простір над елементом і виштовхує його донизу. */
}
Або можна дати опису товара flex-grow: 1 — тоді він виросте, витіснивши кнопку в самий низ.
Обидва підходи є класикою Flexbox-архітектури!
Сьогодні Flexbox покриває 90% потреб у створенні користувацьких інтерфейсів: від кнопок і навігаційних меню до багатошарових карток. Проте іноді з'являється задача: побудувати складний, нерівномірний двовимірний макет (з колонками і рядками, що перетинаються), наприклад, "шахову дошку" або складну галерею. У цьому випадку навіть Flexbox починає ускладнюватись. Коли закінчуються можливості Flexbox, в гру вступає CSS Grid Layout — його ми розглянемо в наступних статтях.
CSS Flexbox: Фундамент гнучких макетів
Глибоке занурення в основи Flexbox: історія виникнення, поняття flex-контейнера та flex-елементів, осі (Main та Cross Axis), flex-direction, flex-wrap, flex-flow та використання gap.
CSS Grid. Двовимірний макет. Частина 1
Глибоке занурення у CSS Grid Layout: display grid, одиниця fr, repeat(), minmax(), auto-fill, auto-fit, розміщення елементів через grid-column та grid-row, gap. Основи двовимірної сітки.