Kurs HTML i CSS · Moduł 4: Flexbox
Flex-grow, Flex-shrink, Flex-basis - precyzyjna kontrola rozmiaru
W tej lekcji7
Witaj znów w Bibliotece Aleksandryjskiej! Na jednej półce leżą trzy zwoje: pierwszy zawsze potrzebuje tyle samo miejsca, drugi może zająć resztę, a trzeci ustąpi, gdy zrobi się ciasno, zupełnie jak menu, treść i panel na stronie. Decydują o tym flex-basis, flex-grow i flex-shrink.
Flex-basis - początkowy rozmiar elementu
flex-basis określa początkowy rozmiar elementu PRZED rozdzieleniem wolnego miejsca. Przyjmuje piksele, procenty, zero albo auto:
.item {
flex-basis: 200px; /* Początkowy rozmiar 200px */
}
.item {
flex-basis: 0; /* Ignoruj rozmiar treści, zacznij od 0 */
}
.item {
flex-basis: auto; /* Użyj rozmiaru treści (default) */
}
.item {
flex-basis: 50%; /* 50% kontenera */
}Zero każe zignorować rozmiar treści. Domyślne auto bierze szerokość elementu, a gdy jej nie ma, rozmiar treści. Czym więc flex-basis różni się od width?
/* Width poza flexboxem jest sztywny */
.item {
width: 200px; /* Poza flexboxem zawsze 200px */
}
/* Flex-basis jest sugestią */
.item {
flex-basis: 200px; /* Około 200px, ale może rosnąć/kurczyć się */
}Poza flexboxem width: 200px to zawsze 200 pikseli. W kontenerze flex obie wartości są punktem startowym: przy flex-basis: auto przeglądarka bierze width, a gdy ustawisz oba, wygrywa flex-basis. To jak blok kamienny przed rzeźbieniem, który można jeszcze powiększyć albo pomniejszyć.
Flex-grow - rosnięcie elementu
flex-grow określa, jak bardzo element może rosnąć w stosunku do innych, gdy zostaje wolne miejsce:
.container {
display: flex;
width: 600px;
}
.item-1 {
flex-grow: 1; /* Zajmie 1 część wolnej przestrzeni */
flex-basis: 100px;
}
.item-2 {
flex-grow: 2; /* Zajmie 2 części wolnej przestrzeni */
flex-basis: 100px;
}
.item-3 {
flex-grow: 1; /* Zajmie 1 część wolnej przestrzeni */
flex-basis: 100px;
}
/* Wolna przestrzeń: 600px - 300px = 300px
Suma flex-grow: 1 + 2 + 1 = 4
item-1: 100px + (300px × 1/4) = 175px
item-2: 100px + (300px × 2/4) = 250px
item-3: 100px + (300px × 1/4) = 175px
*/Wolne 300 pikseli przeglądarka dzieli na cztery części, bo tyle wynosi suma flex-grow. Uwaga: flex-grow: 2 nie robi elementu dwa razy większego, tylko daje mu dwa razy większy kawałek wolnego miejsca. Domyślne flex-grow: 0 oznacza brak wzrostu, 1 wzrost proporcjonalny, a 2 czy 3 dwa lub trzy razy większy udział. Zwykle rośnie tylko treść między panelami:
.layout {
display: flex;
}
.sidebar {
flex-grow: 0; /* Sidebar NIE rośnie */
flex-basis: 250px; /* Sztywny rozmiar 250px */
}
.main-content {
flex-grow: 1; /* Main content zajmie całą pozostałą przestrzeń */
}
.aside {
flex-grow: 0; /* Aside NIE rośnie */
flex-basis: 200px;
}Menu i panel trzymają 250 i 200 pikseli, a treść z flex-grow: 1 zabiera całą resztę.
Flex-shrink - kurczenie się elementu
flex-shrink działa w drugą stronę: określa, jak bardzo element może się kurczyć, gdy brakuje miejsca. Domyślna wartość to 1:
.item {
flex-shrink: 1; /* Element może się kurczyć (default) */
}
.item-important {
flex-shrink: 0; /* Element NIE kurczy się */
}
.item-flexible {
flex-shrink: 3; /* Element kurczy się 3x szybciej niż inne */
}Zero wyłącza kurczenie, a większa liczba oznacza większy udział w oddawaniu miejsca. Sprawdźmy to w kontenerze, w którym brakuje 200 pikseli:
.container {
display: flex;
width: 400px;
}
.item-1 {
flex-basis: 200px;
flex-shrink: 1; /* Kurczy się normalnie */
}
.item-2 {
flex-basis: 200px;
flex-shrink: 2; /* Kurczy się 2x szybciej */
}
.item-3 {
flex-basis: 200px;
flex-shrink: 0; /* NIE kurczy się */
}
/* Razem: 600px, ale kontener ma tylko 400px
Brakuje: 200px
item-3 pozostanie 200px (flex-shrink: 0)
item-1 i item-2 podzielą się kurczeniem proporcjonalnie
*/Trzeci element zostaje przy 200 pikselach, a brakujące 200 oddają dwa pierwsze w proporcji 1 do 2: pierwszy kurczy się do około 133, drugi do około 67 pikseli. Przy kurczeniu liczy się też flex-basis, więc większe elementy oddają więcej. Najczęściej flex-shrink chroni elementy, które nie mogą się zgnieść, jak logo:
.header-logo {
flex-shrink: 0; /* Logo NIGDY się nie kurczy */
width: 150px;
}
.header-nav {
flex-shrink: 1; /* Nawigacja może się kurczyć */
}
.header-search {
flex-shrink: 2; /* Wyszukiwarka kurczy się najszybciej */
}Logo zawsze ma 150 pikseli, a gdy robi się ciasno, wyszukiwarka z flex-shrink: 2 oddaje miejsce chętniej niż nawigacja.
Flex shorthand - łączenie wszystkich trzech
Z poprzedniej lekcji znasz skrót flex, który łączy wszystkie trzy wartości w kolejności grow, shrink, basis:
/* flex: grow shrink basis */
.item {
flex: 1 1 200px;
/* flex-grow: 1, flex-shrink: 1, flex-basis: 200px */
}Tak samo flex: 1 0 200px to flex-grow: 1, flex-shrink: 0 i flex-basis: 200px.
Najczęstsze wartości flex:
W codziennej pracy wystarczy kilka gotowych form, które warto znać na pamięć:
/* flex: 1 - elastyczny element */
.item {
flex: 1;
/* Równoważne: flex: 1 1 0 */
/* Rośnij, kurcz się, zacznij od 0 */
}
/* flex: auto - naturalny rozmiar z elastycznością */
.item {
flex: auto;
/* Równoważne: flex: 1 1 auto */
/* Rośnij, kurcz się, zacznij od rozmiaru treści */
}
/* flex: none - sztywny rozmiar */
.item {
flex: none;
/* Równoważne: flex: 0 0 auto */
/* NIE rośnij, NIE kurcz się */
}
/* flex: 0 0 200px - absolutnie sztywny */
.item {
flex: 0 0 200px;
/* Zawsze 200px, bez rosnięcia/kurczenia */
}
/* flex: 2 - rośnij 2x szybciej */
.item {
flex: 2;
/* Równoważne: flex: 2 1 0 */
}flex: 1 startuje od zera, więc kilka takich elementów dzieli miejsce po równo, a flex: auto startuje od rozmiaru treści, więc dłuższy tekst dostanie szerszą kolumnę. Moja rada: przy równych kolumnach wybieraj flex: 1.
Praktyczne przykłady
1. Dashboard layout - 3 kolumny
Pierwszy układ to panel administracyjny z menu, treścią i widżetami:
<div class="dashboard">
<aside class="sidebar">Sidebar (stały)</aside>
<main class="main-content">Main (elastyczny)</main>
<aside class="widgets">Widgets (stały)</aside>
</div>Menu dostaje sztywne flex: 0 0 250px, treść flex: 1, a panel flex: 0 1 300px, czyli woli 300 pikseli, ale może ustąpić:
.dashboard {
display: flex;
gap: 20px;
height: 100vh;
}
.sidebar {
flex: 0 0 250px; /* Sztywny: 250px */
background: #34495e;
}
.main-content {
flex: 1; /* Zajmie całą pozostałą przestrzeń */
background: white;
overflow-y: auto;
}
.widgets {
flex: 0 1 300px; /* Preferowany: 300px, może się kurczyć */
background: #ecf0f1;
}
/* Na małych ekranach */
@media (max-width: 768px) {
.dashboard {
flex-direction: column;
}
.sidebar,
.widgets {
flex: 0 0 auto; /* Automatyczny rozmiar */
}
}Na telefonie media query ustawia kolumny jedna pod drugą i przywraca panelom naturalny rozmiar przez flex: 0 0 auto.
2. Header responsywny
Drugi przykład to nagłówek z logo, nawigacją, wyszukiwarką i kontem:
<header class="header">
<div class="logo">Logo</div>
<nav class="nav">Nawigacja</nav>
<div class="search">Wyszukiwarka</div>
<div class="user">User</div>
</header>Zwróć uwagę na min-width: 0 przy nawigacji i min-width: 150px przy wyszukiwarce, do których wrócimy przy debugowaniu:
.header {
display: flex;
gap: 20px;
padding: 10px 20px;
background: #2c3e50;
align-items: center;
}
.logo {
flex: 0 0 150px; /* Zawsze 150px */
}
.nav {
flex: 1 1 auto; /* Zajmie dostępną przestrzeń */
min-width: 0; /* Ważne dla kurczenia się */
}
.search {
flex: 0 1 300px; /* Preferuje 300px, może się kurczyć */
min-width: 150px; /* Minimum 150px */
}
.user {
flex: 0 0 100px; /* Zawsze 100px */
}Nawigacja zabiera wolne miejsce, a wyszukiwarka woli 300 pikseli, ale nie zejdzie poniżej 150.
3. Card grid z różnymi rozmiarami
Trzeci przykład to siatka kart w trzech rozmiarach:
<div class="card-grid">
<div class="card small">Small</div>
<div class="card medium">Medium</div>
<div class="card large">Large</div>
<div class="card small">Small</div>
</div>Kontener zawija karty dzięki flex-wrap: wrap, czyli flex-flow: row wrap z poprzedniej lekcji. Małe karty biorą ćwierć szerokości minus część odstępu, średnie połowę, a duże cały rząd:
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card {
height: 200px;
background: #3498db;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
.card.small {
flex: 0 0 calc(25% - 15px); /* 1/4 szerokości minus gap */
}
.card.medium {
flex: 0 0 calc(50% - 10px); /* 1/2 szerokości minus gap */
}
.card.large {
flex: 0 0 100%; /* Pełna szerokość */
}
@media (max-width: 768px) {
.card.small,
.card.medium {
flex: 0 0 100%; /* Pełna szerokość na mobile */
}
}Cztery małe karty wypełniłyby rząd co do piksela, bo cztery razy calc(25% - 15px) plus trzy odstępy po 20 pikseli daje 100%.
Debugowanie Flexbox
Problem 1: Czy width blokuje rosnięcie?
Wielu początkujących uważa, że width przy flex-grow blokuje rosnięcie. Oto taki kod i dwie popularne recepty:
/* Problem */
.item {
flex-grow: 1;
width: 200px; /* To tylko punkt startowy */
}
/* Rozwiązanie */
.item {
flex: 1; /* flex automatycznie nadpisuje width */
/* lub */
flex-grow: 1;
min-width: 200px; /* Użyj min-width zamiast width */
}To mit: w kontenerze szerokim na 600 pikseli dwa takie elementy mają po 300, bo width jest tylko punktem startowym. Kłopot pojawia się, gdy sąsiedzi mają różne szerokości, bo każdy startuje z innego miejsca, a flex: 1 wyrównuje ten start. Jeśli element naprawdę nie rośnie, sprawdź flex-grow, max-width i to, czy rodzic ma display: flex.
Problem 2: Element nie kurczy się
Ten problem jest prawdziwy: element z flex-shrink: 1 uparcie trzyma swoją szerokość:
/* Problem */
.item {
flex-shrink: 1;
min-width: 300px; /* min-width zapobiega kurczeniu! */
}
/* Rozwiązanie */
.item {
flex-shrink: 1;
min-width: 0; /* Pozwól na kurczenie */
}Winne jest min-width. Elementy flex mają domyślnie min-width: auto, więc nie kurczą się poniżej swojej najdłuższej niełamliwej treści, na przykład adresu URL.
Problem 3: Tekst przepełnia kontener
Z tej domyślnej wartości wynika trzeci problem, czyli długi tekst wypychający element poza kontener:
/* Problem */
.item {
flex: 1;
/* Długi tekst może wypchnąć element poza kontener */
}
/* Rozwiązanie */
.item {
flex: 1;
min-width: 0; /* Kluczowe! */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}min-width: 0 pozwala się skurczyć, overflow: hidden ucina nadmiar, a text-overflow: ellipsis dopisuje wielokropek do tekstu trzymanego w jednej linii.
Podsumowanie
flex-basis to elastyczny odpowiednik width, flex-grow z domyślnym 0 decyduje o rośnięciu, a flex-shrink z domyślnym 1 o kurczeniu. Skrót flex łączy wszystkie trzy. W kolejnej lekcji zbudujesz z tych klocków całą siatkę kolumn.
W edytorze poniżej czeka pokaz odstępów, do którego wrócimy przy gap. Dopisz regułę .flex-item:first-child { flex-grow: 1; } i zobacz, jak pierwszy element zabiera wolne miejsce sąsiadom.
Kod do tej lekcji: index.html
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox - Gap</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>↔Gap - Odstępy między elementami</h1>
<p class="subtitle">Nowoczesny sposób na tworzenie odstępów w flexbox</p>
<section class="demo-section">
<h2>Bez gap</h2>
<div class="flex-container no-gap">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
</div>
<p class="description">Elementy bez odstępów</p>
</section>
<section class="demo-section">
<h2>gap: 10px</h2>
<div class="flex-container gap-10">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
</div>
<p class="description">Mały odstęp między wszystkimi elementami</p>
</section>
<section class="demo-section">
<h2>gap: 30px</h2>
<div class="flex-container gap-30">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
</div>
<p class="description">Duży odstęp między elementami</p>
</section>
<section class="demo-section">
<h2>row-gap i column-gap oddzielnie</h2>
<div class="flex-container custom-gap">
<div class="flex-item">1</div>
<div class="flex-item">2</div>
<div class="flex-item">3</div>
<div class="flex-item">4</div>
<div class="flex-item">5</div>
<div class="flex-item">6</div>
</div>
<p class="description">row-gap: 40px, column-gap: 10px</p>
</section>
<section class="demo-section practice">
<h2>Praktyka: Egipska plansza Senet</h2>
<p class="practice-description">Gra Senet - najstarsza gra planszowa z Egiptu</p>
<div class="board-container">
<div class="senet-board">
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
<div class="square empty"></div>
<div class="square">●</div>
<div class="square">●</div>
</div>
</div>
<div class="game-info">
<h3>ℹO grze Senet:</h3>
<ul>
<li>Najstarsza znana gra planszowa (ok. 3100 p.n.e.)</li>
<li>Gra dla 2 osób</li>
<li>Plansza 3x10 pól</li>
<li>Ulubiona gra faraonów</li>
<li>Znaleziona w grobach egipskich</li>
</ul>
</div>
</section>
</div>
</body>
</html>Pamiętaj: flex-grow, flex-shrink i flex-basis to rozkazy faraona dla budowniczych piramid, które mówią, które bloki mają urosnąć, które się zmniejszyć, a które pozostać nietknięte.
Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co robi właściwość flex-grow?
2. Właściwość skrótowa flex: 1 0 200px odpowiada jakiemu zestawowi wartości?
Zadania praktyczne w grze
- Układanie w pionie
Ułóż wartości flex-grow od najmniejszego do największego wzrostu:
- Układanie w poziomie
Ułóż skrótowy zapis flex-flow ustawiający poziomy kierunek z zawijaniem elementów:
- Klikanie w kolejności
Ułóż elementy aby stworzyć poprawną deklarację flex-basis:
- Układanie w poziomie
Ułóż kolejność wartości we właściwości skrótowej flex: