Poznajemy czym jest flexbox, flex-kontener i flex-dziecko. Uczymy się kiedy powinno się używać flexboxa, jakie mieć podejście do używania go oraz przede wszystkim – robimy mini-projekt z prostym paskiem nawigacyjnym z użyciem flexboxa. Do dzieła.

Czym jest flexbox – pozycjonowanie jako rząd lub kolumna

W odróżnieniu od popularnego grida, flexbox nie opiera się o kolumny i wiersze. Flexbox posiada dwie osie (main axis i cross axis) oraz kierunek – albo rząd albo kolumna i te osie zależne są od ustawionego kierunku.

Osobiście nigdy tego nie pamiętam i po prostu wpisuję „na czuja” i zazwyczaj wychodzi, jak nie to wiem, że pomyliłem osie. Tak – w przypadku flexboxa inaczej niż w gridzie czy innych zagadnieniach CSS (jak box model, box sizing) możemy zapomnieć o zrozumieniu jak to działa.

Zrozumieć się nie da, nie dlatego, że jest to specjalnie trudne, ale dlatego, że jest niezbyt transparentne i ogólnie nie opłaca się tego uczyć na pamięć. To trochę jak z wpisywaniem hasła – masz zapisane w odpowiednim miejscu oraz potrafisz je wpisać będąc przy klawiaturze bez patrzenia, ale o powiedzeniu z pamięci jakie masz hasło zapomnij.

To oczywiście moje zdanie. Tym niemniej, flex-rodzic ma takie właściwości:

  • flex-direction
    • Jeżeli row, oś główna biegnie od lewej do prawej, jak oś x na układzie współrzędnych
    • Jeżeli row, cross axis biegdzie z góry do dołu, jak odwrócona oś y z układu współrzędnych
    • Jeżeli column, zamieniamy main i cross ze sobą
  • justify-content – ustawianie elementów na osi głównej
    • Jeżeli start, to początek osi głównej (lewo dla rzędów, góra dla kolumn)
    • Jeżeli end, to koniec osi głównej (prawo dla rzędów, dół dla kolumn)
    • Jeżeli center, to środek – równy odstęp lewo/prawo dla rzędów, równy odstęp góra-dół dla kolumn
    • Jest jeszcze space-between, space-around i space-evenly – to najlepiej wypróbować samemu
  • align-items – ustawianie elementów na cross axis
    • Jeżeli start – góra w przypadku rzędów, lewo w przypadku kolumn
    • Jeżeli end – dół w przypadku rzędów, prawo w przypadku kolumn
    • Jeżeli stretch – elementy rozciągają się od góry do dołu (w rzędzie) lub od lewej do prawej (w kolumnie)
    • Jeżeli cetner – wycentrowanie w pionie (dla rzędów) lub w poziomie (dla kolumn)

To brzmi jak niezły bełkot, a przecież jeszcze nie opuściliśmy w pełni transparentnego terytorium, jeszcze jesteśmy tam, gdzie wszystko działa tak jak jest to opisane.

Mimo wszystko – polecam flexboxa, choć 1000 razy bardziej wolę grida. Przez długi czas miałem do flexboxa po prostu złe podejście, starając się zawsze zaczynać od kartki papieru, rozrysowując layout, stawiając te osie, biorąc to na rozum. Tak, z gridem można się bawić w ten sposób, większość CSSa jest zaskakująco logiczna.

Praca z flexboxem to jednak, w każdym razie w moim przypadku, nie schemat blokowy. To robienie czegoś metodą prób i błędów, a za pomocą flexboxa można to łatwo osiągnąć i dojść do zamierzonego efektu, jeżeli tylko potrafimy na chwilę przestać próbować to zrozumieć i po prostu poeksperymentować.

Flex-rodzic od strony technicznej – centrujemy elementy body

Okej, wstęp za nami, teraz przechodzimy do bardzo prostego ćwiczenia, które wykonamy dla rozgrzewki – centurjemy elementy body w pionie i poziomie. Wygląda to tak:

body {
    height: 100vh;
    background-color: antiquewhite;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}

W przypadku body, musi mieć wysokość. Kierunek row jest domyślny, ale w ten sposób można go wpisywać. Justify-content centruje lewo/prawo (dla rzędów) zaś align-items góra/dół (dla rzędów).

Możemy też wycentrować tylko góra/doł:

body {
    height: 100vh;
    background-color: antiquewhite;
    display: flex;
    flex-direction: row;
    align-items: center;
}

Analogicznie, możemy wycentrować element tylko w pozycji lewo/prawo:

body {
    height: 100vh;
    background-color: antiquewhite;
    display: flex;
    flex-direction: row;
    justify-content: center;
}

Możemy umieścić nasz element (zakładam, że mamy jeden) w lewym górnym rogu:

body {
  height: 100vh;
  display: flex;
  justify-content: flex-start;
 }

Jak wiemy, rząd jest domyślny, zaś dla rzędów oś główna to lewo-prawo, a to główną osią zarządza justify-content. Możemy przesunąć w prawo:

body {
  height: 100vh;
   display: flex;
   justify-content: flex-end;
}

Możemy też dodać ustawienie na osi cross axis, dla rzędu to góra/dół i w ten sposób ustawić element w prawym, dolnym rogu (dla leniwych, cały HTML):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
        height: 100vh;
        display: flex;
        justify-content: flex-end;
        align-items: flex-end;
        }
        .box {
        width: 200px;
        height: 200px;
        background-color: teal;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
</html>

Warto się tym pobawić.

Kiedy używać flexboxa a kiedy nie – moje zdanie

Osobiście odkąd poznałem grida i media queries zawsze chcę używać grida, bo czuję, że to daje pełną kontrolę nad layoutem. Nie jest to jednak dobre podejście.

Flexboxa używamy zawsze, gdy mamy do czynienia z czymś, co jest albo kolumną, albo rzędem albo czymś co raz jest kolumną a raz rzędem.

Wyobraźmy sobie grid elementów typu post. Każdy post to jest post-wrapper, zawierający obrazek-link oraz tytuł link. Wrapper posta jest flex-rodzicem kolumną (obrazek u góry, tytuł na dole) a zarazem elementem grida zajmującym 1/4 szerokości ekranu.

W przypadku gdy ekran się zmniejszył post-wrapper jako element grida zajmuje już całość ekranu, zaś jako flex-rodzic zamienia się w rząd: obrazek z lewej, tytuł po prawej.

To są idealne przykłady użycia flexboxa. Inne to:

  • Nawigacja u góry ekranu (czyli rząd)
  • Ukryta treść hamburger menu (czyli kolumna, choć ja to wolę stosem nazywać)
  • Ukryta treść dropdowna (czyli stos albo rząd jeżeli dropdown ma mieć dzieci-dropdowny)
  • Stopka na dole ekranu
  • Różne elementy będące grid-dzieckiem a jednoczeście grupujące inne elementy jako flex-rodzic

Właściwości flex-dziecka – nie warto się uczyć, warto korzystać

Moja przygoda z flexboxem wyglądała tak, że szybko opanowałem teorię dotyczącą flex-rodzica, ale przy flex-dziecku się pogubiłem. Mamy tam do czynienia z kilkoma właściwościami:

  • flex-basis – teoretycznie domyślna szerokość (dla rzędów) lub wysokość (dla kolumn)
  • flex-grow – teoretycznie ile element może urosnąć jeżeli jest wolne miejsce
  • flex-shrink – teoretycznie o ile element może się skurczyć, jeżeli miejsca brakuje
  • flex – teoretycznie skrót tych 3 właściwości

W praktyce te pierwsze 3 nigdy u mnie nie działały tak, jak były opisane, zaś za skrót się nawet nie ważyłem zabierać, bo skoro wersji dłuższej nie rozumiałem, to co dopiero skrót. I może tylko ja tak mam, ale flexbox jest tą jedną rzeczą, która zawsze będzie robiona „na oko”. Wszystko da się zrozumieć, ale nie to.

Mimo wszystko, udało mi się wypracować patent do pracy z flexboxem, który w żaden sposób nie tłumaczy mi jak on działa, ale dostarcza zawsze tego, czego od flexboxa oczekuję. Wiedząc, że mam bardziej zaawansowanego i całkowicie logicznego grida, ale nie strzela się z armaty do muchy i są sytuacje, gdzie flexbox świetnie pasuje.

Więc flow naszej pracy z flexboxem opieramy o trzy zasady:

  • Zawsze korzystamy na dzieciach z flex, nigdy (albo bardzo rzadko) z flex-basis/grow/shrink
  • Ustawiamy flex na proprocjonalną wielkość jaką chcemy uzyskać
  • Ustawiamy min-width na minimum, poniżej którego nie chcemy, aby element się kurczył

Wszystko to wypróbujemy sobie na ćwiczeniu, w którym zrobimy sobie pasek nawigacyjny.

Pasek nawigacyjny przy pomocy flexboxa – do dzieła

Moglibyśmy zrobić sobie pasek nawigacyjny z logiem po lewej, czterema jednowyrazowymi linkami na prawo od loga witryny i jednym guzikiem zaloguj maksymalnie na prawo osiągniętym za pomocą sztuczki z marginesem ustawionym na auto.

Moglibyśmy zrobić coś fajnego, gdzie akurat flexbox daje więcej radości niż frustracji i z pewnością w przyszłości takie ćwiczenia sobie zrobimy.

Teraz jednak spróbujemy zrobić coś, co nie jest takie oczywiste i zajęło mi to sporo czasu. Szkielet do prawdziwego paska nawigacyjnego, gdzie po lewej, po środku i po prawej wyskakują nam najróżniejsze problemy, jeżeli robimy w bardzo prosty, tutorialowy sposób i w zasadzie mamy ochotę się poddać i już grida użyć.

To tylko szkielet, ale zrozumienie tego da nam możliwość zrobić nawigację flexboxem i mniej więcej rozumieć co się dzieje. Najpierw HTML:

<div class="container">
        <div class="left"></div>
        <div class="middle">
            <button>ClickMe</button>
            <button>ClickMe</button>
            <button>ClickMe</button>
            <button>ClickMe</button>
            <button>ClickMe</button>
            <button>ClickMe</button>
        </div>
        <div class="right"></div>
    </div>

To i tak minimum, w naszym prawdziwym navbarze po lewej będzie logo i nazwa witryny, w środku nieznana ilość przycisków (czasem dwóch długich wyrazów), po prawej formularz z inputem tekstowym i szukaj.

Okej, reset stylów:

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

Teraz body niech będzie flex-rodzicem i niech wszystko zaczyna się od góry:

body {
    height: 100vh;
    background-color: thistle;
    display: flex;
    align-content: flex-start;
}

Teoretycznie nie musimy ustawiać body na flex ale ćwiczymy sobie. Wtedy nasz kontener musi mieć ustawiony max-height, aby nie wylał się na całą wysokość:

.container {
    width: 100%;
    max-height: 100px;
    background-color: teal;
    display: flex;
    flex-wrap: wrap;
}

Robimy to, bo często flex-rodzic jest jednocześnie flex-dzieckiem. Gdyby wykomentować w body display-flex i align-content to nie musielibyśmy określać maksymalnej wysokości.

Teraz left, pierwszy flex-dzieciak:

.left {
    background-color: gold;
    flex: 1.5;
    min-width: 250px;   
}

Flex ustawione na nie-zero oznacza, że element będzie się kurczyć oraz rozciągać na głównej osi (lewo/prawo dla rzędów) w pewnej proprocji do innych elementów (tutaj ma mieć 1.5, zaś middle damy na 4 i right znowu na 1.5).

Ustawione min-width sprawia, że nie będzie się kurczyć (w przypadku rzędów, w kolumnie byłoby min-height) poniżej tej wartości. Tak samo robimy z right:

.right {
    background-color: blueviolet;
    flex:1.5;
    min-width: 250px;
}

Okej, teraz middle, które będzie dzieckiem względem navbara i rodzicem względem guzików:

.middle {
    background-color: tomato;
    /* JAKO DZIECKO */
    flex: 4;
    /* JAKO RODZIC: */
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: space-around;
}

Jako dziecko ustawiamy mu flex na 4. Rozciągaj się tak, aby proporcje były takie, że 1.5 dostają right i left zaś ty 4 w tej samej proporcji. Zwężaj się i to ile wlezie, nie masz określonego minimum (na media query zamienimy ci guziki na ikonę hamburgera w odpowiednim momencie).

Jako rodzic, ma wyświetlać rząd guzików, albo wręcz dwa rzędy. Na osi góra/dół (cross axis dla rzędów) mają być wycentrowane. Na osi głównej lewo/prawo mają te guziki być rozłożone tak, aby miały przestrzeń wokół siebie.

Sprawdzamy jak to się wszystko zmniejsza, zwiększa, rozciąga, teraz możemy przechodzić do stylowania np. zróbmy coś z guzikami:

.middle button {
    padding: 10px 20px;
}

A jak to się robi gridem? Tutaj pseudo-kodem szybko napiszę:

some-grid-parent {
    display: grid;
    grid-template-columns: 250px 1fr 250px;
    grid-template-columns: auto;
}

Lewo i prawo dostają po 250px, środek to, co zostaje. I to bardzo prosty grid, prostacki wręcz, zaś dalej możemy sobie wycentrować i poustawiać za pomocą position czy innego CSSa.

Napisałem ten artykuł ponieważ istnieje wiele tutoriali, które pokazują robienie navbara flexboxem, ale nie pokazują tych wszystkich frustrujących momentów, w których gdy chcesz wyjść odrobinę poza to, co pokazane w tutorialu, wszystko zaczyna się rozjeżdżać.

Generalnie navbar nie jest idealnym przykładem korzystania z flexboxa w dobie resposywności i można sobie więcej problemów narobić, nie rozumiejąc co i jak działa.

Przykład idealny, to stos z obrazkiem i linkiem, który jest grid-dzieckiem 1/4 szerokości strony zaś na mobilnym zamienia się w grid dziecko na całą szerokość strony i wtedy ze stosu (kolumny) zamienia się w rząd z obrazkiem po lewej i tytułem po prawej.

Wtedy za pomocą flex też możemy proporcje obrazka i tytułu ustawić i sobie to ładnie dopasować, że na jednym media query (tym samym, które zamienia post z powiedzmy 1/2 strony na całość strony) post-wrapper zmienia flex-direction z kolumny na wiersz.

Ustawiwszy flex dla tytułu i obrazka możemy osiągnąć proporcje i to, że będzie się zmniejszać/zwiększać i w zasadzie mamy responsywność i na żadnej szerokości, na żadnym modelu źle to nie wygląda, a wcale się nie narobiliśmy.

W przyszłości zrobimy sobie takie ćwiczenia. Do następnego razu.

Kluczowe elementy, co dalej i wzorce pracy z flexboxem

Kluczowe elementy flex-rodzica do zapamiętania:

  • W przypadku rzędu (domyślny kierunek) oś główna leci od lewej do prawej – ma to logiczny sens
  • W przypadku kolumny oś główna leci od góry do dołu – również ma to sens
  • W przypadku osi głównej elementy możemy pozycjonować na początek, koniec, na środku oraz kilka innych opcji:
    • space-between czyli elementy na skrajnych końcach, maksymalna przestrzeń między nimi
    • space-around, czyli przestrzeń wokół elementów
    • space-evenly, czyli przestrzeń między elementami oraz margines całej grupy elementów po równo (łatwiej to wypróbować w praktyce)
  • W przypadku osi pobocznej jak ją nazywam (cross axis) mamy możliwość ustawiania elementów na początku, końcu, środku, lub możliwość rozlania ich na całą wysokość (rząd) lub szerokość (kolumna) tej osi

Kluczowe elementy flex-dziecka do zapamiętania:

  • Nadaj każdemu dziecku flex 1 a na osi głównej będą się rozszerzać i zwężać proporcjonalnie
  • Nadaj im flex większy niż 1 w odpowiedniej proporcji do innych flexów, aby otrzymać jako-takie proporcje dzieci na osi głównej względem siebie
  • Nadaj min-width (w przypadku rzędu) lub min-height (kolumna) aby ustalić minimum, poniżej którego dziecko nie ma prawa się zmniejszyć

Kolejne rzeczy do ogarnięcia:

  • Nauczyć się, jak odsuwać elementy od siebie korzystając z margin auto, co tworzy pewien odstęp, który jest pustym miejscem, które się zmniejsza
  • Nauczyć się jak działa flex wrap i nowrap
  • Nauczyć się align-content, które ustawia rzędy wrapujących się elementów
  • Nauczyć się, jak działa align-self, który ustawia jeden element inaczej na osi pobocznej

Ciekawe wzorce:

  • Flex dziecko jednocześnie flex rodzicem innych elementów
  • Grid dziecko flex rodzicem innych elementów
  • Flex kolumna zamieniana na flex rząd na odpowiednim breakpoincie
  • Flex rząd zamieniany na flex kolumnę na odpowiednim breakpoincie

Wcale nie musimy z flexboxa korzystać. Ale możemy.