Wykonamy kilka ćwiczeń z flexboxem, aby zrozumieć jego podstawowe możliwości. Zamieniamy wiedzę teoretyczną i pewne nieuporządkowane podstawy w praktykę za pomocą kilku prostych ćwiczeń.

Wycentruj elementy w pionie i poziomie

Jeden element-dziecko (box), jeden rodzic (body). Zadanie – wycentruj w pionie i poziomie ten kwadrat:

<body>
    <div class="box"></div>
</body>

Reset stylów:

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

Kwadrat 200 na 200 o jakimś kolorze:

.box {
    height: 200px;
    width: 200px;
    background-color: tomato;
}

Body jako flex-rodzic:

body {
    height: 100vh;
    background-color: teal;
    display: flex;
    /*domyślnie:
    flex-direction: row; */
    justify-content: center;
    align-items: center;
}

Body musi mieć wysokość. Domyślnie flex-direction to row, oś główna to lewo/prawo (jest jeszcze row-reverse, który ma prawo/lewo). Justify-content ustawia w centrum na osi lewo/prawo, align-items ustawia w centrum na osi góra/dół.

Wycentruj lewo/prawo, doklej do góry ekranu

Oto nasza pseudo-nawigacja:

<div class="nav">
        <button>ClickMe</button>
        <button>ClickMe</button>
        <button>ClickMe</button>
        <button>ClickMe</button>
</div>

Startowy CSS:

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

.nav {
    width: 100%;
    height: 200px;
    background-color: teal;
    display: flex;
    /* domyślnie
    flex-direction: row;
    align-items: stretch; */
}

Domyślnie flex-direction to row, czyli oś główna lewo/prawo, oś poboczna góra/dół. Domyślnie align-items ustawione na stretch, czyli na osi pobocznej wszystko rozciąga się, rozlewa się, zatem nasze guziki są od góry do dołu.

Mają być doklejone do góry, a zatem:

.nav {
    width: 100%;
    height: 200px;
    background-color: teal;
    display: flex;
    /* domyślnie
    flex-direction: row; */
    align-items: flex-start;
}

Na osi pobocznej (góra/dół) przestały się rozciągać i zostały doklejone do początku tej osi (do góry). Teraz na osi lewo/prawo pora je wycentrować:

.nav {
    width: 100%;
    height: 200px;
    background-color: teal;
    display: flex;
    /* domyślnie
    flex-direction: row; */
    align-items: flex-start;
    justify-content: center;
}

Trzy responsywne elementy o równych proprocjach

Mamy pseudo-nawigację:

<div class="nav">
        <div class="item"></div>
        <div class="item"></div>
        <div class="item"></div>
</div>

Chcemy trzy elementy o równych proporcjach, które zwężają się i rozszerzają. Najpierw flex-rodzic:

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

.nav {
    width: 100%;
    height: 200px;
    background-color: teal;
    display: flex;
    
}

Teraz dzieci:

.item:nth-of-type(1) {
    flex: 1;
    background-color: gold;
}

.item:nth-of-type(2) {
    flex: 1;
    background-color: tomato;
}
.item:nth-of-type(3) {
    flex: 1;
    background-color: green;
}

Jeżeli chcemy mieć minimum, możemy ustawić dowolnemu dziecku min-width. Jeżeli chcemy inne proporcje – możemy proporcjonalnie zwiększać wartość flex elementu danego dziecka (proporcjonalnie względem innych flexów).

Tak wygląda prosty przykład 1/4 1/2 1/4:

.item:nth-of-type(1) {
    flex: 1;
    background-color: gold;
}

.item:nth-of-type(2) {
    flex: 2;
    background-color: tomato;
}
.item:nth-of-type(3) {
    flex: 1;
    background-color: green;
}

Gridowym odpowiednikiem byłoby 1fr 2fr 1fr w grid-template-columns.

Hamburger hidden column – flex-kolumna

Do naszego pseudo-navbara dodajemy kolumnę, która ma się wyświetlać na mobilnych urządzeniach po kliknięciu w hamburgera:

<div class="nav">
        <div class="item"></div>
        <div class="item"></div>
        <div class="item"></div>
    </div>
    <div class="hamburger-column">
        <button>O NAS</button>
        <button><a href="">COŚTAM</a></button>
        <button><a href="">COŚTAM</a></button>
        <button><a href="">COŚTAM</a></button>
        <button><a href="">COŚTAM</a></button>
</div>

Ma zajmować całą szerokość. Elementy mają rozlewać się na całą szerokość kolumny, mieć domyślną wysokość 80px.

.hamburger-column {
    width: 100%;
    background-color: blueviolet;
    display: flex;
    flex-direction: column;
    /* domyślne */
    /* align-items: stretch; */
}

Szerokość na 100%. Kierunek – kolumna, zatem oś poboczna to lewo/prawo. Align-items domyślnie ustawione na stretch działa na osi pobocznej – rozlewa elementy na lewo i prawo.

Teraz element:

button {
    flex-basis: 80px;
    background-color: aliceblue;
}

Jako że element jest kolumnowy to oś główna to góra/dół. Flex-basis to domyślna wielkość osi głównej czyli domyślna wysokość 80px. Na osi lewo-prawo rozlewają się, jak należy.

Możemy jeszcze ustawić text-align, które nie ma nic wspólnego z flexboxem, ale ustawi tekst naszego guzika na lewo:

button {
    flex-basis: 80px;
    background-color: aliceblue;
    text-align: left;
}

Kostka 4 i 6 – space-between, kolumny i rzędy

Budujemy flexboxową kostkę, pokazującą liczbę 4. Kostka to rząd 2 kolumn, każda zawiera dwa doty:

<div class="dice">
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
</div>

Podstawowe style kostki i reset stylów ogólnych CSS:

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

.dice {
    width: 400px;
    height: 400px;
    border: 1px solid black;
    border-radius: 25%;
    background-color: thistle;
}

Podstawowe style kropki (no takiej prawie kropki) na naszej kostce:

.dot {
    width: 40px;
    height: 40px;
    background-color: black;
    border-radius: 25%;
}

Odpychamy dzieci naszej kostki od siebie:

.dice {
    (...)
    display: flex;
    justify-content: space-between;
    padding: 30px;
}

Jako że kostka to rząd oś główna to lewo/prawo, zatem justify content tutaj odpycha nasze 2 elementy-dzieci na osi lewo/prawo od siebie. Jedna kolumna po lewej, druga po prawej.

Teraz nasze kolumny, odepchnijmy dzieci od siebie:

.column {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

Jako że flex-direction mamy kolumnowy oś główna idzie od góry do dołu. Justify-content odpycha te elementy na osi góra/dół dając nam ładną kostkę. Nic, tylko dodać odrobinę paddingu:

.dice {
(...)
 padding: 30px;
}

Kostkę 6 zrobimy sobie zaraz „po Bożemu”, ale na razie spróbujmy przerobić to co mamy na kostkę 6, to też cenna umiejętność. Robimy to w HTML:

<div class="dice">
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
    </div>

Mamy 3 kolumny po 2 elementy, kolumny i elementy odepchnięte od siebie. Taka kostka 6 bokiem, ale to można łatwo rozwiązać:

.dice {
(...)
    transform: rotate(90deg);
}

Kostka 6 poprawne podejście – 2 kolumny po 3 elementy

Pomyślmy, jak wygląda kostka 6. Mamy jeden rząd 2 kolumn (odepchniętych od siebie lewo/prawo), każda kolumna po 3 kropki, odepchnięte od siebie góra/dół. Nasz HTML będzie wyglądać tak:

<div class="dice">
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
        <div class="column">
            <div class="dot"></div>
            <div class="dot"></div>
            <div class="dot"></div>
        </div>
</div>

Główne style:

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

.dice {
    width: 400px;
    height: 400px;
    border: 1px solid black;
    border-radius: 25%;
    background-color: thistle;
    (...)
    padding: 30px;
}
.dot {
    width: 40px;
    height: 40px;
    background-color: black;
    border-radius: 25%;
}

Odpychamy kolumny kostki od siebie:

.dice {
    (...)
    display: flex;
    justify-content: space-between;
}

Skoro kostka to rząd (domyślny flex-direction) to oś główna to lewo/prawo. Justify-content odpycha nasze 2 dzieci (po 3 doty każdy) na osi lewo/prawo.

Teraz kolumna, która wygląda tak samo jak poprzednio

.column {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

Skoro kolumna, to oś główna góra/dół. Skoro tak, justify-content odpycha elementy na osi góra/dół od siebie. Mamy kostkę 6.

Kostka 2 – align-self dla ustawiania indywidualnie osi pobocznej

Potrzebny nam rząd odsuniętych od siebie elementów, dokładnie dwóch:

<div class="dice">
        <div class="dot"></div>
        <div class="dot"></div>
</div>

Teraz kostka:

.dice {
    (...)
    display: flex;
    justify-content: space-between;
}

Rząd, czyli oś główna to lewo/prawo. Justify-content odsuwa elementy lewo/prawo od siebie. Align-items działałoby na osi pobocznej góra/dół i możemy te doty przenieść na dół:

.dice {
    width: 400px;
    height: 400px;
    border: 1px solid black;
    border-radius: 25%;
    background-color: thistle;
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    padding: 30px;
}
.dot {
    width: 40px;
    height: 40px;
    background-color: black;
    border-radius: 25%;
}

Problem w tym, że tylko 1 ma być na dole (ten drugi). Więc albo za pomocą align-self cofniemy pierwszy do góry (align-self nadpisuje align-content, które działa na osi pobocznej, która dla rzędów jest góra/dół):

.dice {
(...)
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    padding: 30px;
}
.dot {
    width: 40px;
    height: 40px;
(...)
}
.dot:nth-of-type(1) {
    align-self: flex-start;
}

Albo skorzystamy z domyślnego align-items, którym jest stretch, który tutaj nie rozciąga elementów, bo mają one określoną wysokość i szerokość, więc siedzą u góry i ten drugi można w dół opuścić:

.dice {
(...)
    display: flex;
    justify-content: space-between;
    /* align-items: domyślny */
    padding: 30px;
}
.dot {
    width: 40px;
    height: 40px;
    background-color: black;
    border-radius: 25%;
}
/* .dot:nth-of-type(1) {
    align-self: flex-start;
} */
.dot:nth-of-type(2) {
    align-self: flex-end;
}

Kostka 3 – bawimy się dalej z align-self

Robimy jeden rząd po 3 doty pod kostkę 3:

<div class="dice">
        <div class="dot"></div>
        <div class="dot"></div>
        <div class="dot"></div>
</div>

Podstawowe style:

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

.dice {
    width: 400px;
    height: 400px;
    border: 1px solid black;
    border-radius: 25%;
    background-color: thistle;
}
.dot {
    width: 40px;
    height: 40px;
    background-color: black;
    border-radius: 25%;
}

Tworzymy flex-rodzica będącego rzędem z kostki:

.dice {
(...)
    display: flex;
    justify-content: space-between;
    padding: 30px;
}

Jako że mamy do czynienia z rzędem, oś główna idzie lewo/prawo. Justify content pracuje na osi głównej odpychając lewo-prawo kropki od siebie.

Oś poboczna to w przypadku rzędu góra/dół i tam chcemy mieć nasze kropki w centrum:

.dice {
(...)
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 30px;
}

Skoro kostka jest rzędem, to oś poboczna idzie góra/dół zaś align-items na osi pobocznej kropki centruje. Na osi głównej dla rzędu czyli lewo/prawo są od siebie odepchnięte.

Teraz kropka pierwsza musi być w lewym, górnym rogu, zatem:

.dot:nth-of-type(1) {
    align-self: flex-start;
}

Rząd – oś główna lewo/prawo, poboczna góra/dół. Align-items – ustawianie na osi pobocznej. Align-self – nadpisywanie align-items. Ustawiamy element idealnie na początku osi pobocznej czyli na górze.

Teraz ostatni element przenosimy na koniec osi pobocznej czyli na dół w przypadku rzędu:

.dot:nth-of-type(3) {
    align-self: flex-end;
}

Ace of spades – podsumowanie ćwiczeń

Tworzymy naszego asa pik:

<div class="card">
        <div class="top-left">
            <span>A</span>
            <span>♠</span>
        </div>
        <div class="middle">
            <span>♠</span>
        </div>
        <div class="bottom-right">
            <span>A</span>
            <span>♠</span>
        </div>
    </div>

Podstawowe style:

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

 .card {
    width: 400px;
    height: 800px;
    background-color: aliceblue;
 }

Ustawiamy display-flex, które będzie robiło nam rząd:

 .card {
(...)
 display: flex;
 }

Skoro rząd, to oś główna idzie lewo/prawo. Skoro tak, to justify-content działa na osi lewo/prawo. Skoro tak, odpychamy na osi lewo prawo elementy od siebie:

 .card {
(...)
display: flex;
justify-content: space-between;
 }

Skoro rząd, to oś poboczna idzie góra/dół. Skoro tak, align-items działa na osi pobocznej. Skoro tak, ustawiamy elementy w centrum osi góra/dół:

 .card {
(...)
    display: flex;
    justify-content: space-between;
    align-items: center;
 }

Teraz na osi góra/dół pierwsze dziecko (top-left) cofamy na górę, zaś ostatnie (bottom-right) opuszczamy na dół:

 .top-left {
    align-self: flex-start;
 }
 .bottom-right {
    align-self: flex-end;
 }

Robimy z nich flex-kolumny dla spanów, aby pik znajdował się pod asem:

 .top-left {
    align-self: flex-start;
    display: flex;
    flex-direction: column;
 }
 .bottom-right {
    align-self: flex-end;
    display: flex;
    flex-direction: column;
 }

Ujdzie, w końcu ćwiczymy pozycjonowanie. Teraz rotujemy bottom-right, aby wyglądało jak w rzeczywistości:

 .bottom-right {
    align-self: flex-end;
    display: flex;
    flex-direction: column;
    transform: rotate(180deg);
 }

Normalnie używalibyśmy ikon nie znaków unicode, ale ćwiczymy pozycjonowanie, nie wygląd, więc to nie taki problem.