Budujesz dashboard: header, sidebar, główna treść, footer. Sięgasz po Flexbox, bo to narzędzie layoutowe, które znasz, a dwadzieścia minut później masz trzy zagnieżdżone kontenery display: flex, sidebar, który nie utrzyma swojej szerokości bez magicznej liczby, i footer, który unosi się w połowie strony, gdy treści jest mało. To nie jest błąd Flexbox — to Ty używasz narzędzia jednowymiarowego do problemu dwuwymiarowego.
Flexbox rozkłada przestrzeń wzdłuż jednej linii: wiersza albo kolumny. Grid ustawia elementy w wierszach i kolumnach naraz, a te wiersze i kolumny odnoszą się do siebie. Większość zamieszania wokół Grid kontra Flexbox wynika z przeskoczenia tej różnicy.
Flexbox jest jednowymiarowy, Grid jest dwuwymiarowy
Elementy flex żyją na jednej osi, ustawionej przez flex-direction. Przy flex-direction: row każde dziecko ustawia się od lewej do prawej (albo od prawej do lewej, przy row-reverse); przy column elementy układają się od góry do dołu. Istnieje też oś poprzeczna, na której działa align-items. Ale Flexbox nigdy nie wymusza, żeby element z jednej linii ustawił się w jednej linii z elementem z innej. Nawet gdy elementy się zawijają, każda zawinięta linia jest układana samodzielnie.
| |
Elementy Grid siedzą w wierszach i kolumnach, które definiujesz z góry, i możesz ustawić pozycję oraz rozmiar każdego elementu względem obu osi naraz.
| |
1fr to jednostka fr (fraction unit) — udział w dostępnej przestrzeni. Przeglądarka najpierw odlicza stałe tory (240px), a potem dzieli to, co zostało, między tory fr. Czyli grid-template-columns: 1fr 2fr daje drugiej kolumnie dwa razy większy udział w pozostałej przestrzeni, a nie dwa razy większą całkowitą szerokość.
Jak zbudować navbar za pomocą Flexbox
Navbar to podręcznikowy przykład layoutu jednowymiarowego: logo, kilka linków i przycisk w jednej linii, z przestrzenią rozdzieloną między nimi.
| |
| |
justify-content: space-between przypina logo do lewej krawędzi, a przycisk do prawej, z listą linków pośrodku. align-items: center trzyma wszystkie trzy elementy wycentrowane w pionie, nawet jeśli logo jest wyższe niż przycisk. Lista linków jest własnym kontenerem flex, więc jej odstępy pochodzą z gap i nigdy nie dotykasz marginesu.
Zmniejsz viewport i nic się nie zawinie, bo flex-wrap domyślnie ma wartość nowrap: elementy kurczą się do swojej minimalnej szerokości treści, a potem wychodzą poza navbar. Dodaj flex-wrap: wrap i elementy, które się nie mieszczą, spadają do drugiej linii. Ta druga linia jest układana niezależnie i nie ustawi się pod niczym z pierwszej. Dla navbara to nie problem. Dla layoutu, w którym wiersze muszą się ze sobą ustawiać, to sygnał, że trafiłeś na granicę tego, co robi Flexbox.
Jak zbudować layout dashboardu za pomocą CSS Grid
Nazwane obszary grid pozwalają czytać layout strony bez tłumaczenia numerów wierszy i kolumn w głowie.
| |
Każda nazwa w grid-template-areas musi tworzyć prostokąt, a te stringi są obrazem strony: widzisz, że sidebar obejmuje wszystkie trzy wiersze, po prostu patrząc na CSS. Sidebar ma stałe 240px, header i footer mają jawne wysokości, a main zabiera wszystko, co zostało, bo jego wiersz to 1fr. Footer siedzi na dole viewportu nawet na krótkiej stronie, bez absolutnego pozycjonowania i bez JavaScript, bo min-height: 100vh na kontenerze plus wiersz 1fr robią to za Ciebie.
Wersja tej samej strony we Flexbox potrzebuje dwóch kontenerów: wiersza flex na sidebar plus treść, a potem kolumny flex wewnątrz treści na header, main i footer, z flex: 1 na main, żeby zepchnąć footer na dół. Działa. Tylko że stawiasz dwa layouty jednowymiarowe jeden na drugim, żeby udawały jeden dwuwymiarowy, i każda zmiana struktury strony oznacza dotykanie obu.
Responsywna siatka kart bez media queries
Ten jeden wzorzec zastępuje mnóstwo boilerplate’u z media queries: siatkę kart, która dodaje albo zdejmuje kolumny wraz ze zmianą viewportu.
| |
repeat(auto-fit, minmax(220px, 1fr)) mówi przeglądarce, żeby zmieściła tyle kolumn o szerokości co najmniej 220px, ile się da w wierszu, a potem rozciągnęła je, aby wypełniły wiersz. Kontener 1400px dostaje pięć kolumn po około 260px; telefon dostaje jedną kolumnę na całą szerokość. Żadnego breakpointa.
auto-fit i auto-fill różnią się tylko wtedy, gdy masz mniej kart, niż wypełniłoby cały wiersz. auto-fit zwija puste tory, więc prawdziwe karty rozciągają się na cały wiersz. auto-fill zachowuje puste tory, więc karty zostają tej szerokości, jaką miałyby w pełnym wierszu, a resztę wiersza wypełnia pusta przestrzeń. Użyj auto-fill, gdy szerokość karty ma zostać taka sama niezależnie od liczby elementów, jak w siatce produktów, gdzie trzy wyniki nie powinny rozdąć się do trzeciej części strony każdy. Użyj auto-fit, gdy pół pustego wiersza wygląda gorzej niż szersze karty, jak na dashboardzie z dwoma albo trzema kaflami podsumowania.
Jak używać Grid i Flexbox razem
Rzadko wybierasz jedno z nich na całą stronę. Grid obsługuje szkielet, Flexbox obsługuje jednowymiarową treść wewnątrz każdego regionu.
| |
Każda .card siedzi w komórce grid i rozciąga się na jej wysokość, bo elementy grid domyślnie się rozciągają. Wewnątrz niej header, treść i footer tworzą jedną kolumnę, więc justify-content: space-between ustawia footer na dole każdej karty, niezależnie od tego, jak dużo tekstu ma każda z nich. To jest działająca reguła: Grid dla wszystkiego, co musi odnosić się jednocześnie do wiersza i kolumny, Flexbox dla treści, która płynie wzdłuż jednej linii, zagnieżdżony wszędzie tam, gdzie Grid go umieścił.
Jeśli używasz Grid, żeby przełączać się między widokiem kart i widokiem listy, zapisz wybór odwiedzającego w localStorage. To jedno wywołanie setItem, a wybór przetrwa przeładowania i zamknięte karty bez rundy do serwera.
Kiedy użyć subgrid dla zagnieżdżonych siatek
Grid wewnątrz elementu grid zaczyna własny zestaw torów, niezależny od toru rodzica. To się psuje, gdy chcesz, żeby wewnętrzne wiersze karty (obrazek, tytuł, opis, cena) ustawiały się w jednej linii w całym rzędzie kart. Ustaw grid-template-rows: subgrid (albo grid-template-columns: subgrid) na zagnieżdżonym elemencie grid, a on wykorzysta tory rodzica, zamiast definiować nowe — wtedy wiersz tytułu każdej karty ustawi się w linii z sąsiadami, choć każda karta jest wciąż własnym kontenerem grid. Subgrid działa we wszystkich aktualnych głównych przeglądarkach, więc możesz go używać od razu, zamiast wymuszać równe wysokości sztywnymi wartościami.
Kiedy Flexbox jest lepszym wyborem, nawet jeśli Grid by sobie poradził
Grid może ułożyć pojedynczy wiersz dzięki grid-auto-flow: column. Ale dla treści, która naprawdę jest jednowymiarowa, Flexbox ma coś, czego Grid nie ma: flex-grow i flex-shrink pozwalają każdemu elementowi zająć więcej albo mniej z pozostałej przestrzeni, w zależności od jego własnej treści. To właśnie potrzebne dla pola wyszukiwania, które powinno zająć całą przestrzeń, jaką zostawią przycisk i ikona o stałej szerokości. Domyślnie wybieraj Grid, gdy wiersze i kolumny muszą się ze sobą odnosić; używaj Flexbox, gdy układasz jedną linię i potrzebujesz rozrastania i kurczenia się poszczególnych elementów, a nie tylko wyrównania.
Ten błąd działa też w drugą stronę. Ludzie budują layout wielokolumnowy z flex-wrap: wrap plus procentowym width na każdym elemencie, żeby udawać grid. Trzyma się to, dopóki ostatni wiersz nie ma mniej elementów niż poprzednie. Wtedy space-between rozrzuca dwa ostatnie elementy do przeciwnych krawędzi, albo flex-grow rozciąga je szerzej niż wszystko powyżej. Przeglądarka wciąż rozdziela elementy wzdłuż linii, nie wypełnia siatki. Jeśli liczysz kolumny, użyj Grid.
Jeśli wciąż potrzebujesz JavaScript przy resize, na przykład żeby przestawić tooltip obok karty, dodaj debounce do handlera zamiast odpalać go przy każdym zdarzeniu resize, które wywołuje przeglądarka.
Grid czy Flexbox: jak wybrać
Jedno pytanie o tym decyduje: elementy mają wiersze i kolumny, które muszą się ze sobą ustawiać, czy to jedna linia elementów?
| Sytuacja | Użyj |
|---|---|
| Navbar, grupa przycisków, toolbar | Flexbox |
| Szkielet strony: header, sidebar, main, footer | Grid |
| Responsywna siatka kart albo produktów | Grid (auto-fit/auto-fill + minmax) |
| Centrowanie jednego elementu albo równe rozmieszczenie kilku | Flexbox |
| Layout formularza z wyrównanymi etykietami i polami | Grid |
| Treść wewnątrz jednej komórki grid (header/body/footer karty) | Flexbox, zagnieżdżony w Grid |
Następnym razem, gdy otwierasz arkusz stylów dla nowego layoutu, naszkicuj najpierw boksy. Jeśli rysujesz linie, które się przecinają, to Grid. Jeśli każdy boks siedzi na jednej linii, to Flexbox.