CSS Flexbox

Dodane przez tdudkowski - czw., 15/11/2018 - 16:27

Flexbox jest niesamowicie elastyczny i daje nowe możliwości, ale jest przy tym dość nieintuicyjny i w procesie wdrażania się wymaga repetycji w większym stopniu niż np. Boxmodel czy Float. Zachęcam do zapoznania się z nim w tym artykule: A Complete Guide to Flexbox.

Podstawową cechą niezbędną dla zrozumienia jak działa Flexbox to orientacja działania wzdłuż osi głównej i poprzecznej (możemy myślec o tym jak o układzie odniesienia) oraz elastyczne zarządzanie przestrzenią, zarówno jej nadmiarem i brakiem. Tylko rozumiejąc poprawnie te dwa założenia możemy zrozumieć Flexboksa i poprawnie go stosować.

Oś główna
Tworzy układ odniesienia modelu; w zasadzie matematycznie rzecz biorąc powinniśmy mówić o wektorze, bo ma kierunek i zwrot. Wzdłuż osi głównej rozmieszczone są obiektu: ich kolejność, wielkość, ułożenie muszą być widziane przede wszystkim w relacji do osi głównej. Drugą osią tego układu odniesienia jest oś poprzeczna.
Zarządzanie przestrzenią
Flexbox jest modelem elastycznym i tam gdzie kontener dysponuje większą przestrzenią niż suma rozmiarów zawartych elementów (dzieci) dynamicznie i łatwo ją rozdziela, albo przydzieklając ją elementom albo rozmieszczając ją między nimi. Tak samo jest w przypadku kiedy elementy (dzieci) nie mieszczą się w kontenerze, mogą zmiejszyć się proporcjonalnie, wyjść poza kontener lub utworzyć kolejny rząd elementów.

Dlatego niniejszy artykuł składa się z następujących części:

  1. Oś - układ odniesienia
  2. Przestrzeń na osi, zarządzanie nadmiarową przestrzenią i jej brakiem
  3. Właściwości elementów kontenera
  4. Podstawowe przykłady (nie wszystkie jeszcze są)
  5. Odnośniki

Na wstępie trzeba wyjaśnić trzy rzeczy:

  1. Żeby w danym kontenerze wprowadzić Flexbox trzeba użyć display:flex, które "włącza" go w kontenerze (istnieje też dużo rzadziej używany inline-flex liniowa wersja Flexboxa).
  2. Kiedy w kontenerze jest display:flex, wtedy model Flexboxa stosuje się do kontenera i wszystkich jego bezpośrednich dzieci, ale - uwaga! - ta cecha nie jest dziedziczona; żeby zawrzeć Flexboxa we Flexboksie trzeba tym dzieciom też dać display:flex.
  3. Nadal możemy używać wszystkich cech CSS dotyczących rozmiarów, marginesów, paddingów itd.

1. Oś - układ odniesienia

Wszystko co jest związane z ułożeniem i rozmieszczeniem obiektow w Flex można zrozumieć tylko w kontekscie głównej osi, a nie kierunków absolutnych dotychczas stosowanych w CSS. Oś może być poprowadzona poziomo lub pionowo. Mamy więc po display: flex kolejną dyrektywę czyli flex-direction:

  • row - poziomo, od lewej do prawej (domyślna)
  • row-reverse - poziomo, od prawej do lewej
  • column - pionowo, z góry na dół
  • column-reverse - pionowo, od dołu do góry

Dlatego w przypadku Flexboxa nie mówimy o lewej lub prawej stronie, a o początku i końcu osi.

Drugą opcją związaną z prowadzeniem osi jest flex-wrap. To jest wybór pomiędzy jedną linią osi (jednym wierszem) a zawijaniem osi po dojściu do końca kontenera. Najlepiej to zrozumieć na przykładzie liter. Bez zawijania wszystkie muszą się zmieścić w jednej linii. Z zawijaniem po dojściu do końca kontenera tworzą nową linię o tym samym kierunku i zwrocie, lub przeciwnym zwrocie. Tak więc flex-wrap:

  • nowrap (domyślna)
  • wrap - zawijanie z zachowaniem zwrotu wektora
  • wrap-reverse - zawijanie ze zmianą zwrotu wektora za każdym razem

Czyli, żeby użyć przykładu:
div {display:flex; flex-direction:row-reverse; flex-wrap:wrap-reverse;} wyświetli wszystkie elementy kontenera z prawej do lewej, zawijając, w każdym kolejnym wierszu zmieniając zwrot.

Domyślne ustawienie elementów Flexboxa (samo display: flex) to jedna linia z lewej do prawej, bez przejścia do następnej linii.

2. Przestrzeń na osi

To tyle jeśli chodzi o poprowadzenie osi. A teraz rozmieszczenie elementów kontenera. Mamy trzy cechy:

justify-content
rozmieszczenie elementów wzdłuż osi głównej (ma zastosowanie tylko jeśli rodzic ma wiecej przestrzeni niż dzieci w sumie, bo zarządza wolną przestrzenią)
align-items
rozmieszczenie elementów w poprzek osi głównej
align-content
rozmieszczenie wielu osi w poprzek osi głównej (dotyczy oczywiście tylko sytuacji kiedy dozwolone jest zawijanie)

justify-content rozmieszczenie wzdłuż osi:

  • flex-start: wszystkie elementy są wyrównane do początku osi (domyślne)
  • flex-end: wszystkie elementy są wyrównane do końca osi
  • center: centrowanie
  • space-between: elementy są rozmieszczane odpowiednio pierwszy na początku, ostatni na końcu osi, ewentualny środkowy centrowany, jeżeli jest ich więcej niż trzy to podobnie jak w space-evenly, z tą różnicą, że skrajne przylegają do krawędzi kontenera
  • space-around: każdy element otaczany jest równo rozdzieloną wolną przestrzenią, co oznacza że pierwszy i ostatni od krawędzi będzie dzieliła jedna jednostka wolnej przestrzeni, a pomiędzy elementami będą dwie jednostki wolnej przestrzeni
  • space-evenly: wszystkie elementy są rozmieszczone tak, że przestrzeń pomiędzy nimi oraz pomiędzy nimi i krawędziami kontenera jest równa.

align-items rozmieszczenie w poprzek osi

  • stretch: są rozciągane na cały rozmiar poprzeczny kontenera, ale cały czas z zachowaniem min-width/max-width (domyślne)
  • flex-start: elementy są przyciągane do początku osi poprzecznej
  • flex-end: elementy są przyciągane do końca osi poprzecznej
  • center: centrowanie
  • baseline: elementy są wyrównane do ich linii bazowej

align-content rozmieszczenie wielu osi; wszystkie opcje są dokładnie takie same jak w align items i należy je rozumieć analogicznie (stretch rozciąga element na całą wysokość linii), nawet ta sama jest domyślna; ale oprócz nich są jeszcze dwie:

  • space-between: pierwsza linia jest przyciągnięta do początku osi poprzecznej, a ostatnia do jej końca
  • space-around: linie są rozmieszczone równo z równym rozdzieleniem przestrzeni pomiędzy nimi

Skrót:
flex-flow: flex-direction, flex-wrap (domyślne: row, nowrap).

Tak więc mamy jedno polecenie włączające Flexboxa i pięć dyrektyw dotyczących zawartości kontenera oraz skrót. Podobnie mamy pięć dyrektyw dotyczących elementów oraz ich skrót.

3. Właściwości elementów kontenera

  • order: argument to liczba całkowita (czyli może też być ujemna) określa kolejność umieszczenia elementów, domyślna dla każdego jest 0 co oznacza rozmieszczenie ich wg kolejności w kodzie HTML, każda inna niż zero wyrzuca poza porządek narzucony kolejnością w kodzie HTML, wyświetlanie zaczyna się od najniższych liczb.
  • flex-grow: liczba naturalna, rozdziela dodatkową przestrzeń, domyślna 0 oznacza "zachowaj wielkość, nie rośnij"; dla watości innej niż 0 rosną proporcjonalnie do sumy liczb, następuje podział wolnej przestrzeni na sumę liczb i proporcjonalne jej przydzielenie
  • flex-shrink: dotyczy sytuacji braku przestrzeni, argument to liczba naturalna, mechanizm działa analogicznie jak w przypadku flex-grow, z tym, że domyślna jest 1, która oznacza zmniejsz się proporcjonalnie; 0 to "nie zmniejszaj się", a każda inna niż 0 lub 1 jak wyżej decyduje o proporcjach zmniejszania się, w przeciwieństwie do dotychczasowych modeli tutaj nie może nastąpić zmniejszenie poniżej rozmiaru narzuconego zawartością elementu
  • flex-basis: wielkość elementu na osi głównej, domyślny argument to auto co oznacza wielkość wynikającą z zawartości, zawsze ma wyższość względem wszystkich innych dyrektyw dotyczących tego rozmiaru, ale nigdy nie zmniejszy elementu poniżej rozmiaru wynikającego z zawartości (czyli auto to jest minimum)
  • align-self: może dla danego elementu zmienić wartość align-item wynikającą z kontenera, domyślna to auto (czyli miej to co kontener narzuca), pozostałe to: flex-start, flex-end, center, baseline i stretch

Skrót
flex: flex-grow, flex-shrink, flex-basis; domyślne to 0 1 auto. Uwaga: drugi i trzeci parametr jest opcjonalny, ale jeżeli zostaną pominięte w zapisie, narzucone zostaną wartości domyślne.

4. Przykłady

Podstawowe zasady:

  • minimum kodu, zarówno HTML jak i CSS.
  • tylko standardowe rozwiązania, żadnych obejść i trików
  • tylko model blokowy i flex, żadnego floatu i grida.
  • jest elastyczny i działa na wszystkich przeglądarkach

Schemat nagłówek, treść, stopka

Zacznijmy od podstaw: flex jest świetnym narzędziem, ale służy do tworzenia układów jednowymiarowych. Do dwuwymiarowych mamy grida, o którym może później. To ograniczenie oznacza, że nie możemy powiedzieć elementom HTML, żeby będąc w jednym pojemniku układały się jedne pionowo, a inne poziomo. W klasycznym układzie strony mamy trzy podstawowe elementy: nagłówek, treść i stopkę ułożone linearnie, pionowo od góry do dołu. Treść natomiast składa się z przynajmniej dwóch elementów umieszczonych horyzontalnie. Dla flexa oznacza to konieczność użycia dodatkowego pojemnika/wrappera, który będzie tym środkowym elementem zawierającym treść (dopiero w gridzie jest to niepotrzebne).

Mamy więc:

  • nagłówek <header>
  • treść <main>
  • stopkę <footer>

W tym dla rozmieszczenia tych elementów wystarczy dla body {display: flex; flex-direction: column; min-height: 100vh;}

Pierwsze polecenie wprowadza model flex, drugie układa wszystkie elementy pojemnika pionowo, trzecie zapewnia zajęcie całego okna przegladarki..

Na razie jednak wszystkie mają tę samą wysokość. Żeby to zmienić dla nagłówka i stopki wprowadzamy min-height (zapewnia to większą elastyczność układu niż samo height), a dla wewnętrznego pojemnika dla treści flex: 1;. I to na początek naprawdę wszystko. Wystarczą trzy elementy HTML i cztery (lub sześć - zależy jak liczyć) parametrów CSS, żeby stworzyć taki bazowy układ strony. Bez żadnych sztuczek footer ląduje na samym dole okna, a blok treści zajmuje całą dostępną przestrzeń. Jeśli treści jest więcej footer bez żadnych problemów jest na samym dole dokumentu.

codepen.io: Sticky Footer [Flexbox]

Układ dwukolumnowy asymetryczny

Tu mamy dwa elementy:

  • pasek boczny (sidebar) <aside>
  • artykuł <article>

Nie trzeba deklarować dla nich układu wiersza flex-direction: row;, bo jest on domyślny. Warto jednak wiedzieć, że wystarczy deklaracja odwróconego wiersza flex-direction: row-reverse;, żeby odwrócić kolejność wyświetlania. W ten sposób bez zmian w kodzie HTML możemy przerzucić pasek boczny z lewej na prawą stronę.

Pasek boczny powinien mieć stałą szerokość, a artykuł powinien wypełniać pozostałą szerokość ekranu.

Dla paska bocznego deklarujemy maksymalną szerokość, co zapewnia wypełnienie zaprojektowanej dla niego szerokości. Oprócz tego flex: 1;.
Wygląda więc to tak: sidebar {flex: 1; max-width: [];}

Artykuł wypełniający cała pozostałą szerokość i wysokość tworzy się takim samym parametrem - flex: 1; i na tym w zasadzie można by skończyć jeżeli mowa o samym układzie strony.

W praktyce ten układ, choć spełnia wszystkie założone kryteria, ma jedną poważną wadę. Otóż treść artykułu razem z nim wypełnia całą pozostała szerokość ekranu, co nawet przy standardowych współczesnych monitorach powoduje rozlanie jej zdecydowanie zbyt szeroko. Szerokość treści można ograniczyć dając zadeklarowaną szerokość dla wszystkich jej elementów (np. article > p {max-width: [];}), ale po pierwsze jest to niewygodne, a poza tym co jeżeli będziemy potrzebowali jednorodnego tła dla treści i czegoś zupełnie neutralnego dla marginesu? Dlatego potrzebujemy drugiego pustego pojemnika na treść artykułu, dajmy na to, że będzie to <div class="article-content">.

Mamy więc układ bloku treści:

  • pasek boczny (sidebar) <aside>[treść paska bocznego]</aside>
  • artykuł <article>
  • blok treści artykułu <div class="article-content">[treść artykułu]</div>
  • </article>

Niezbyt skomplikowane, prawda?

I dla tego wszystkiego następujący CSS:

aside {flex:1; max-width: [];}
article {flex: 1; display: flex;}
div.article-content {flex: 1; max-width: [];}

I teraz to już naprawdę wszystko. Podsumowując: w pełni zgodny ze standardami, typowy układ dwukolumnowy można osiągnąć używając jedynie czterech elementów HTML (w tym tylko dwóch "nadmiarowych") i siedmiu parametrów CSS.

codepen.io: 2 Column Layout CSS Flexbox

Układ trójkolumnowy asymetryczny

Wszystko jest tak jak w powyższym przykładzie układu dwukolumnowego, z tym tylko wyjątkiem, że wprowadzamy dodatkowy element - właśnie tę trzecią kolumnę.

Będziemy mieć więc następujący układ:

  • lewy pasek boczny <aside class="sidebar-one">[treść lewego paska bocznego]</aside>
  • artykuł <article>
  • blok treści artykułu <div class="article-content">[treść artykułu]</div>
  • </article>
  • prawy pasek boczny <aside class="sidebar-two">[treść prawego paska bocznego]</aside>

Tutaj akurat żadne zmiany w CSS nie są potrzebne. Przyda się jednak najpierw określić minimalną szerokość drugiego (w tym wypadku prawego) paska bocznego, a potem minimalną szerokość bloku treści artykułu, tak żeby zsumowane z lewym paskiem bocznym dawały rozmiar breakpointu.

Podobnie jak w układzie dwukolumnowym kolejność wyświetlania kolumn można zmienić parametrem flex-direction: row-reverse;, lub ustalić ją dowolnie dla każdej z kolumn dając odpowiedni parametr order: [];.

codepen.io: 3 Column Layout CSS Flexbox

Układ z jednakowymi kolumnami

Wyrzucamy z bloku treści wszystkie nasze obiekty. W układach gdzie kolumny są sobie równe, mamy:

  • <div class="one">
  • <div class="two">

Natomiast w CSS (zakładając, że mamy ten sam jak w poprzednich przykładach schemat) dajemy tylko: main {flex: 1; display: flex;} a dla zawartych w kontenerze divów main div {flex: 1;}. I to całkowicie wystarcza. Flex automatycznie rozmieszcza zawarte w pojemniku elementy.

I jeszcze na zakończenie dwie dość oczywiste uwagi: po pierwsze, jeżeli tak jak w powyższym przykładzie zastosujemy uniwersalny selektor określając po prostu każdy div bezpośrednio wewnątrz pojemnika możemy dodać teoretycznie dowolną liczbę kolumn, flex będzie rozmieszczał je proporcjonalnie. Tak więc dodanie kolejnego bloku treści, kolejnej kolumny w layoucie jest po prostu dodaniem kolejnego diva i żadnej zmiany w kodzie ani HTML (oprócz dodania diva) ani CSS już nie trzeba.

Po drugie tak jak o tym była mowa w układach asymetrycznych, dokładnie w ten sam sposób z samego tylko CSS-a możemy zarządzać kolejnością wyświetlania kolumn, albo odwracając ją przez row-reverse, albo bezpośrednio we właściwościach obiektów przez order: [];.

codepen.io: 2 [or more] Column Layout [all equal] CSS Flexbox

Menu

Załóżmy, że mamy menu w HTML zbudowane z odnośników zawartych w elementach nienumerowanej listy. Chcemy by niezależnie od szerokości elementu menu wszystkie odnośniki dzieliły jego szerokosć po równo między siebie. Jest to coś co używając starego CSS-a można by zdefiniować np. w ten sposób: ul.menu>li {float: left; display: block; width: calc(100%/x);} (gdzie x to liczba elementów listy) i jeszcze byśmy byli zadowoleni, że tylko jedna zmiana w CSS wystarczy by dopasować kod do zmiany liczby elementów menu. Tymczasem we Flexboksie nic nie trzeba zmieniać, sam ułoży elementy proporcjonalnie. Ale po kolei.

Najpierw dla całej listy deklarujemy użycie Flexboksa oraz wyłączamy wyróżniki listy, które mogą we Fleksie wchodzić w sąsiadów i wyłączamy lewy padding: ul.menu {display: flex; list-style-type: none; padding-left: 0;}.

Dla elementów listy można by ustawić flex-direction: row;, ale ponieważ jest to opcja domyślna można ją pominąć, zaniast tego ustawiamy współdzielenie nadmiarowej przestrzeni: ul.menu li {flex: 1;}. I to w zasadzie wszystko jeżeli chodzi o układ menu, oczywiście dla samych odnośników przydaje się określić wyświetlanie jako element blokowy i wycentrowanie tekstu: {display: block; text-align: center;}. I to już naprawdę wszystko. Jak więc widać dla utworzenia takiego menu wystarczą cztery dyrektywy, z tego bezpośrednio związane z rozmieszczeniem są tylko dwa.

Warto też pamiętać, że w media query, dla mniejszych rozdzielczości dla listy trzeba ustawić wyświetlanie kolumny zamiast wiersza, czyli ul.menu {flex-direction: column;}.

codepen.io: Proportional Menu [Flexbox]

Układ liniowy dla mniejszych rozdzielczości

Dla mniejszych rozdzielczości układ robimy przez media query, np: @media (max-width:800px) {[]}. I tu najważniejszą zmianą, którą musimy wprowadzić, wspólną dla wszystkich wymienionych powyżej układów jest nadanie elementom bloku treści układy kolumny: div.main-content {flex-direction: column;}.

Jednakowe również jest ustawienie sposobu wyświetlania treści jeśli jest jej mniej niż wynosi wysokość ekranu. Jeżeli chcemy by treść była wyświetlana tylko w rzeczywistej długości dla body dajemy parametr display:blok;, a jeżeli chcemy żeby zawartość strony zajęła cały ekran nawet jeśli nie ma dość treści to wybieramy display:flex;.

I te dwie rzeczy to jest naprawdę wszystko co trzeba zrobić dla układów o jednakowych kolumnach. Natomiast w przypadku układów asymetrycznych jedyne co trzeba dodatkowo zrobić to skorygować szerokości ustalone dla szerszych ekranów.

Czyli w dwukolumnowym będzie to aside {min-width: 100%; max-width: 100%;} oraz div.article-content {min-width: auto;}. W trzykolumnowym jak wyżej plus aside.second-aside {min-width: 100%;}.

Karta

Tak jak na samym początku napisałem, Flexbox jest jednowymiarowym sposobem opisu układy treści. Jest linearny. Można jednak używając standardowych metod Flexboksa stworzyć typowy układ karty z dwoma wierszami treści, z których jeden jest jedno- a drugi wieloelementowy. Wystarczy użyć opcji {flex-direction: column; flex-wrap:wrap;} i odpowiednio dla jednoelementowego (np. tytuł) dać width: 100%;, a dla wieloelementowego ustawić albo flex:1; albo użyć zwykłych parametrów CSS.

codepen.io: Title-Photo-Txt Element [Flexbox]

Po utworzeniu klas odpowiadających elementom o dobranej wielkości można tworzyć karty składające się z sekwencji jedno- i wieloelementowych segmentów, bez dodatkowych kontenerów. W poniższym przykładzie można też zobaczyć jak używając calc-a nadać odpowiednie wielkości poszczególnym kartom.

codepen.io: Flex Card [Flexbox]

TODO

5. Odnośniki

Flexbox:

Grid

Do zrobienia:

  • ilustracje i ostylowanie
  • trochę o historii
  • przykłady

Tagi