Flexbox układa elementy w jednej osi (w rzędzie albo w kolumnie), a CSS Grid w dwóch naraz (w wierszach i kolumnach). Flexboxa używaj do menu, pasków narzędzi i wyśrodkowania, a Grida do układu całej strony i siatek kart. Oba można łączyć: Grid na układ strony, a Flexbox wewnątrz poszczególnych elementów.
Flexbox i CSS Grid to dwa narzędzia do układania elementów na stronie, które zastąpiły dawne sztuczki z float i tabelami. Początkujący często pytają, którego z nich użyć, a potem tracą godziny na układ, który „sam się rozjeżdża”. W tym artykule pokażę podstawy obu, kiedy wybrać który i skąd biorą się najczęstsze problemy. Wszystkie przykłady sprawdziłem w przeglądarce, a podane wymiary to wyniki pomiaru, a nie wyliczenia „na oko”.
Czym różnią się Flexbox i CSS Grid?
Najważniejsza różnica to liczba osi:
| Flexbox | CSS Grid | |
|---|---|---|
| Układ | jednowymiarowy (jedna oś: rząd albo kolumna) | dwuwymiarowy (wiersze i kolumny) |
| Skąd bierze rozmiary | z zawartości elementów („od środka”) | z zadeklarowanej siatki („od zewnątrz”) |
| Włączasz go | display: flex na rodzicu | display: grid na rodzicu |
| Typowe zastosowanie | menu, pasek narzędzi, karta, wyśrodkowanie | układ strony, siatka kart, formularze |
Oba działają według tej samej zasady: ustawiasz właściwość na rodzicu (kontenerze), a jego bezpośrednie dzieci stają się elementami układu. Wnuki nie są już objęte tym układem.
Jak działa Flexbox? Osie i wyrównanie
Po wpisaniu display: flex dzieci układają się w rzędzie. Masz wtedy dwie osie: główną (domyślnie pozioma) i poprzeczną (pionowa). Wyrównanie na każdej z nich ustawiają dwie właściwości:
justify-content– wyrównanie wzdłuż osi głównej,align-items– wyrównanie wzdłuż osi poprzecznej.
<style>
.k { display: flex; width: 300px; height: 100px; justify-content: center; align-items: flex-end; }
.k > div { width: 50px; height: 20px; }
</style>
<div class="k"><div>1</div><div>2</div></div>
Pomiar: elementy stoją na środku poziomo (lewy brzeg pierwszego na x = 100) i przy dolnej krawędzi (y = 80, czyli 100 − 20). Teraz zmień kierunek osi głównej na kolumnę (flex-direction: column):
<style>
.k { display: flex; flex-direction: column; width: 300px; height: 100px; justify-content: center; align-items: flex-end; }
.k > div { width: 50px; height: 20px; }
</style>
<div class="k"><div>1</div><div>2</div></div>
Teraz justify-content: center centruje elementy w pionie (pierwszy ma y = 30), a align-items: flex-end przesuwa je w prawo (x = 250). Właściwości zostały te same, ale zamieniły się kierunki, bo osie zamieniły się miejscami. To najczęstsze źródło pomyłek, a samo zagadnienie opisaliśmy też w artykule o myleniu właściwości justify- i align- we flexboxie.
Jak dzielić miejsce we Flexboxie? flex: 1, rosnące i kurczące się elementy
Elementy flexboxa potrafią rosnąć (flex-grow) i kurczyć się (flex-shrink), żeby wypełnić kontener. Skrót flex: 1 oznacza „weź swoją część wolnego miejsca”. Liczba to waga:
<style>
.k { display: flex; width: 600px; }
.k > div { height: 10px; }
.a { flex: 1; }
.b { flex: 2; }
.c { width: 100px; }
</style>
<div class="k"><div class="a"></div><div class="b"></div><div class="c"></div></div>
Pomiar szerokości: 167 px, 333 px i 100 px. Element .c ma stałe 100 px, a pozostałe 500 px dzielą się w stosunku 1 : 2. Drugą stroną jest kurczenie. Gdy elementy nie mieszczą się w kontenerze, flexbox domyślnie je zmniejsza:
<style>
.k { display: flex; width: 300px; }
.k > div { height: 10px; width: 200px; }
.n { flex-shrink: 0; }
</style>
<div class="k"><div></div><div class="n"></div></div>
Dwa elementy po 200 px w kontenerze 300 px: pierwszy skurczył się do 100 px, a drugi z flex-shrink: 0 został przy 200 px (razem 300 px, czyli dokładnie tyle, ile ma kontener).
Jak zawinąć elementy do kolejnego wiersza? flex-wrap i gap
Domyślnie flexbox nie zawija elementów. Zamiast przenieść element do nowej linii, ściska wszystkie w jednej. To odpowiedź na popularne pytanie „dlaczego mój flexbox się nie zawija”:
<style>
.k { display: flex; width: 300px; gap: 10px; }
.k > div { width: 120px; height: 10px; }
</style>
<div class="k"><div></div><div></div><div></div></div>
Trzy elementy po 120 px w kontenerze 300 px zostały ściśnięte do 93 px każdy. Dopisz flex-wrap: wrap, a trzeci element przeskoczy do drugiego wiersza, zachowując swoje 120 px:
<style>
.k { display: flex; flex-wrap: wrap; width: 300px; gap: 10px; }
.k > div { width: 120px; height: 10px; }
</style>
<div class="k"><div></div><div></div><div></div></div>
Właściwość gap ustawia odstępy między elementami, dzięki czemu nie potrzebujesz marginesów, które trzeba osobno zerować na krawędziach.
Jak działa CSS Grid? Kolumny, fr i grid-template-areas
W Gridzie najpierw definiujesz siatkę na rodzicu, a potem elementy wpadają do kolejnych komórek. Jednostka fr oznacza „jedną część wolnego miejsca”. Element może też zająć kilka komórek, np. dzięki grid-column: span 2:
<style>
.g { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; width: 620px; }
.g > div { height: 20px; }
.s { grid-column: span 2; }
</style>
<div class="g"><div class="s"></div><div></div><div></div><div></div><div></div></div>
Pomiar: trzy kolumny po 200 px (620 − 2 × 10, podzielone na 3), a pierwszy element zajmuje dwie kolumny i ma 410 px (2 × 200 + odstęp). Dla całych układów stron wygodny jest zapis obszarami:
<style>
.g {
display: grid; width: 600px; height: 300px;
grid-template-columns: 150px 1fr;
grid-template-rows: 50px 1fr 40px;
grid-template-areas:
"naglowek naglowek"
"menu tresc"
"stopka stopka";
}
.n { grid-area: naglowek; }
.m { grid-area: menu; }
.t { grid-area: tresc; }
.s { grid-area: stopka; }
</style>
<div class="g"><div class="n"></div><div class="m"></div><div class="t"></div><div class="s"></div></div>
Nagłówek i stopka rozciągają się na całą szerokość (600 px), menu ma 150 px, a treść zajmuje resztę (450 px). Kod czyta się niemal jak szkic strony.
Jak zrobić responsywną siatkę bez media queries? auto-fit i auto-fill
Zapis repeat(auto-fit, minmax(150px, 1fr)) mówi: „zmieść tyle kolumn o szerokości co najmniej 150 px, ile się da, i rozciągnij je”. Przy zmianie szerokości okna liczba kolumn zmienia się sama. Dwa słowa kluczowe różnią się zachowaniem przy małej liczbie elementów:
<style>
.g { display: grid; gap: 10px; width: 800px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.g > div { height: 20px; }
</style>
<div class="g"><div></div><div></div></div>
Dla dwóch elementów w kontenerze 800 px sprawdziłem oba warianty:
| Zapis | Szerokość elementu |
|---|---|
repeat(auto-fit, minmax(150px, 1fr)) | 395 px (elementy rozciągają się na całą szerokość) |
repeat(auto-fill, minmax(150px, 1fr)) | 152 px (zostają puste kolumny po prawej) |
auto-fit zwija puste kolumny, więc elementy się rozciągają, a auto-fill je zostawia. Większe zmiany układu, np. przejście z trzech kolumn do jednej z innym menu, nadal rozwiązuje się zwykle media queries.
Flexbox czy Grid? Kiedy użyć którego
Prosta zasada: jeśli układasz elementy w jednej linii (nawet zawijanej), sięgnij po Flexbox. Jeśli myślisz o wierszach i kolumnach naraz, użyj Grida. Poniżej typowe sytuacje:
| Sytuacja | Lepszy wybór | Dlaczego |
|---|---|---|
| Menu poziome, pasek narzędzi | Flexbox | jeden rząd, rozmiar zależy od zawartości |
| Wyśrodkowanie jednego elementu | Flexbox lub Grid | oba zrobią to w dwóch linijkach |
| Układ całej strony (nagłówek, menu, treść, stopka) | Grid | dwa wymiary i obszary |
| Siatka kart lub zdjęć | Grid | równe kolumny bez kombinowania |
| Karta (obrazek, tytuł, przycisk w pionie) | Flexbox (column) | jedna oś, przycisk można „dociągnąć” do dołu |
| Formularz z etykietami po lewej i polami po prawej | Grid | kolumny wyrównane między wierszami |
W prawdziwych projektach używa się obu naraz. Grid układa duże bloki strony, a Flexbox porządkuje wnętrze każdego z nich, np. przyciski w pasku.
Uwaga mentora: nie wybieraj narzędzia dlatego, że „Grid jest nowszy”. Zadaj sobie jedno pytanie: układam elementy w jednej linii czy w siatce? Linia to Flexbox, siatka to Grid. W praktyce i tak użyjesz obu naraz.
Czy zawsze potrzebuję Flexboxa albo Grida?
Nie. To częsty błąd początkujących, którzy po poznaniu Flexboxa dopisują display: flex do każdego kontenera. Jeśli elementy mają stać jeden pod drugim, wystarczy zwykły układ blokowy, który przeglądarka stosuje domyślnie dla elementów takich jak div, p czy section:
<style>
.k { width: 300px; }
.k > div { height: 20px; margin-bottom: 10px; }
</style>
<div class="k"><div>Tytuł</div><div>Opis</div><div>Przycisk</div></div>
Pomiar: trzy elementy ułożyły się w pionie (y = 0, 30, 60), każdy na całą szerokość kontenera (300 px). Dokładnie taki sam układ da display: flex; flex-direction: column; gap: 10px, tylko po co komplikować kod, skoro efekt jest identyczny.
Flexbox w kolumnie jest wart użycia dopiero wtedy, gdy potrzebujesz czegoś, czego układ blokowy nie umie, np. „docisnąć” ostatni element do dołu kontenera o określonej wysokości:
<style>
.k { display: flex; flex-direction: column; width: 300px; height: 200px; }
.k > div { height: 20px; }
.k > div:last-child { margin-top: auto; }
</style>
<div class="k"><div>Tytuł</div><div>Opis</div><div>Przycisk</div></div>
Przycisk stoi teraz na y = 180, czyli przy dolnej krawędzi kontenera 200 px. Bez Flexboxa, w zwykłym układzie blokowym, ten sam kontener zostawia go zaraz pod opisem (y = 40). Zasada jest prosta: najpierw sprawdź, czy wystarczy zwykły display: block, a Flexboxa albo Grida dodaj dopiero wtedy, gdy rzeczywiście potrzebujesz wyrównania, zawijania lub siatki. Krótszy CSS łatwiej utrzymać i trudniej w nim o niespodzianki.
Jak wyśrodkować element w poziomie i w pionie?
To jedno z najczęściej wyszukiwanych pytań o CSS. Masz trzy proste sposoby, a wynik jest identyczny. W kontenerze 400 × 200 px element 100 × 50 px ląduje w każdym z nich w tym samym miejscu (x = 150, y = 75):
/* 1. Flexbox */
.k { display: flex; justify-content: center; align-items: center; }
/* 2. Grid: najkrótszy zapis */
.k { display: grid; place-items: center; }
/* 3. Flexbox + automatyczne marginesy elementu */
.k { display: flex; }
.k > div { margin: auto; }
Wyśrodkowanie w pionie działa tylko wtedy, gdy kontener ma wysokość. Jeśli jej nie ustawisz, kontener ma tylko tyle, ile zajmuje zawartość, i nie ma co centrować. W teście kontener bez height miał 50 px wysokości (tyle co element), więc align-items: center nic nie zmieniał. Ustaw height, min-height (np. min-height: 100vh) albo wysokość odziedziczoną od rodzica.
Przy okazji: margin-left: auto na jednym elemencie flexboxa „odpycha” go na koniec osi. Tak powstaje klasyczne menu z logo po lewej i przyciskami po prawej:
<style>
.k { display: flex; width: 500px; }
.k > div { width: 50px; height: 10px; }
.p { margin-left: auto; }
</style>
<div class="k"><div></div><div></div><div class="p"></div></div>
Trzeci element stoi na x = 450, czyli przy prawej krawędzi kontenera 500 px.
Dlaczego element „rozpycha” układ we Flexboxie? min-width: 0
Elementy flexboxa mają domyślnie minimalną szerokość równą szerokości swojej zawartości (min-width: auto). Jeśli w środku jest długi tekst bez spacji, element nie skurczy się poniżej niego, nawet gdy kontener jest węższy. Wyświetlenie wielokropka (text-overflow: ellipsis) wymaga zawężonego elementu, więc często „nie działa”:
<style>
.k { display: flex; width: 300px; }
.t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
</style>
<div class="k">
<div class="w"><div class="t">bardzodlugitekstbezspacji_bardzodlugitekstbezspacji_bardzodlugitekstbezspacji</div></div>
</div>
Pomiar: element .w (bezpośrednie dziecko flexboxa) ma 739 px w kontenerze 300 px, więc wielokropek się nie pojawia, bo .t ma tyle miejsca, ile potrzebuje. Rozwiązaniem jest jedna linijka na elemencie flexboxa:
.w { min-width: 0; }
Po jej dodaniu .w ma 300 px, a tekst w środku zostaje obcięty z wielokropkiem. Jeśli sam element flexboxa ma overflow: hidden, to ograniczenie min-width: auto w ogóle nie obowiązuje, dlatego problem pojawia się zwykle wtedy, gdy ograniczasz dopiero zagnieżdżony element.
Dlaczego kolumna 1fr w Gridzie robi się za szeroka? minmax(0, 1fr)
W Gridzie to samo zjawisko występuje pod inną postacią. Zapis 1fr oznacza w rzeczywistości minmax(auto, 1fr), czyli kolumna nie zejdzie poniżej szerokości zawartości. Długi tekst rozpycha kolumnę i zgniata sąsiednią:
<style>
.g { display: grid; grid-template-columns: 1fr 1fr; width: 400px; }
.t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
</style>
<div class="g">
<div><div class="t">bardzodlugitekstbezspacji_bardzodlugitekstbezspacji_bardzodlugitekstbezspacji</div></div>
<div>B</div>
</div>
Dwie kolumny po 1fr w kontenerze 400 px wyszły 739 px i 10 px (druga kolumna została ściśnięta do samego odstępu). Wystarczy zamienić 1fr na minmax(0, 1fr):
.g { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
Wtedy kolumny mają po 200 px. To dobre rozwiązanie, gdy w komórkach Grida pojawiają się długie adresy, tabele albo bloki kodu.
Dlaczego ostatni wiersz wygląda inaczej we Flexboxie niż w Gridzie?
Flexbox układa wiersze niezależnie od siebie. Jeśli zawiniesz pięć elementów z flex: 1 1 30%, ostatni wiersz zawiera tylko dwa, które rozciągną się na całą szerokość. Grid zachowuje kolumny:
| Układ (kontener 640 px, 5 elementów, odstęp 10 px) | Szerokość elementów w drugim wierszu |
|---|---|
Flexbox: flex: 1 1 30% z flex-wrap: wrap | 315 px (rozciągnięte) |
Grid: repeat(3, 1fr) | 207 px (w kolumnach jak wyżej) |
Jeśli zależy Ci na równej siatce, użyj Grida. Flexbox lepiej sprawdza się, gdy to właśnie rozciągnięcie jest oczekiwane.
Jakie błędy z Flexboxem i Gridem zdarzają się najczęściej?
| Błąd | Skutek | Jak naprawić |
|---|---|---|
mylenie justify- i align- po zmianie flex-direction | wyrównanie „w złą stronę” | pamiętaj: justify- to oś główna, align- poprzeczna |
display: flex tam, gdzie elementy mają stać jeden pod drugim | zbędna komplikacja kodu | zostaw zwykły układ blokowy |
brak flex-wrap: wrap | elementy ściśnięte w jednej linii | dopisz flex-wrap: wrap |
brak min-width: 0 na elemencie flexboxa | tekst rozpycha układ, brak wielokropka | min-width: 0 na elemencie flexboxa |
1fr zamiast minmax(0, 1fr) | kolumna rozpychana zawartością | zamień na minmax(0, 1fr) |
| wyśrodkowanie w pionie bez wysokości kontenera | element nie jest w środku | ustaw height lub min-height |
display: flex na „wnuku” | układ nie działa | układ obejmuje tylko bezpośrednie dzieci rodzica |
zmiana kolejności przez order lub pozycjonowanie w Gridzie | kolejność Tab i czytnika ekranu zostaje według kodu HTML | zmieniaj kolejność w HTML, a nie tylko wizualnie |
Ostatni punkt to wiedza z dokumentacji, której nie sprawdzałem testem. Przy układach, w których kolejność wizualna różni się od kolejności w kodzie, zawsze przejdź stronę klawiszem Tab.
Podsumowanie
Flexbox układa elementy w jednej osi, a Grid w dwóch. Do menu, pasków i wyśrodkowania wybierz Flexbox, do układu strony i siatek kart Grid, a w większych projektach używaj obu naraz. Gdy układ „się rozjeżdża”, najczęściej winne są: brak zawijania, brak min-width: 0, 1fr zamiast minmax(0, 1fr) albo kontener bez wysokości. Jeśli chcesz poznać frontend od podstaw z mentorem, zobacz nasz kurs JavaScript.


