Zrozumieć Flexboxa
Tworzenie layoutu
To już piąty rozdział kursu o flex-boxie. Wiesz już wszystko, co jest potrzebne, aby stworzyć swój pierwszy layout na flexie. Zacznijmy więc od początku:
Uprościłem nasz kod HTML do minimum - zachowałem jedynie ładowanie czcionki montserrat oraz ustawianie jej jako domyślnego font-family na body w pliku global.css.
Przypomnę, że docelowo chcemy, aby nasza witryna wyglądała mniej więcej tak:
Stwórzmy więc strukturę HTML dla pierwszego elementu - górnego paska nawigacyjnego. Będzie to znacznik nav, wewnątrz którego umieścimy listę nawigacyjną oraz kontener z brandingiem witryny.
Górny pasek nawigacyjny
Całemu paskowi nadamy klasę .navbar, natomiast sekcja z brandingiem, na którą składać się będzie logo oraz nazwa witryny otrzyma nazwę .navbar-branding. Liście nadamy klasę .navbar-list.
Element .navbar-branding będzie składał się z obrazka znajdującego się pod ścieżką 'img/logo.svg' obok którego pojawi się nazwa firmy.
Elementy listy .navbar-list będą zawierały w sobie odnośnik do pozostałych podstron naszej witryny - oczywiście na ten moment adresy te nigdzie nie prowadzą.
Do pliku index.html dołączymy od razu nowoutworzony plik navbar.css, by w nim zapisywać reguły dotyczące paska nawigacyjnego. Wykorzystamy do tego oczywiście znacznik link w sekcji head naszego dokumentu.
Zacznijmy więc stylować nasz element. Na początek nadamy mu display: flex; oraz kolor tła na biały w formacie heksadecymalnym, czyli background-color: #ffffff;
Następnie usuniemy domyślne marginesy z listy nienumerowanej .navbar-list oraz ukryjemy jej domyślne punktory.
Chcielibyśmy oczywiście, aby lista układała się od lewej do prawej - jak wiemy, wystarczy nadać jej regułę display: flex;.
Z designu możemy odczytać, że wysokość naszego paska nawigacyjnego powinna wynosić 60px oraz mieć margines wewnętrzny równy 20px z lewej oraz z prawej. Wysokość ustawiamy oczywiście za pomocą reguły height: 60px a margines wewnętrzny przy pomocy padding: 0 20px.
Super! Wyrównajmy elementy znajdujące się w znaczniku nav w cross-axis przy pomocy align-items: center;. Pamiętaj, że domyślnym flex-direction na elementach stylowanych za pomocą flexa jest row, dlatego main-axis biegnie w osi poziomej, a cross-axis w osi pionowej.
Dodatkowo, nadałem klasę koduje-flex-tip na znaczniku nav, tak, byś po najechaniu na niego kursorem w podglądzie mógł podejrzeć kierunek wybranych osi.
Pamiętaj, że klasę koduje-flex-tip możesz dodać na dowolnym elemencie, w którym chciałbyś zobaczyć tę wizualizację.
Świetnie, nasze elementy są teraz wyrównane w pionie. Jak mogłeś zauważyć, elementy nie mają jednak żadnej przestrzeni między sobą - a w designie widać wyraźnie, że branding ma być dociągnięty do lewej strony kontenera, z kolei .navbar-list do prawej.
Wykorzystajmy justify-content, aby zarządzać przestrzenią w osi poziomej main-axis. Efekt, o który nam chodzi, uzyskamy za pomocą wartości space-between na elemencie .navbar.
Oczywiście, jeśli nie będziemy mieli wystarczająco dużo dostępnej przestrzeni, elementy zaczną się kurczyć, co wynika z domyślnej wartości flex-shrink na dzieciach flexboxa.
Jeśli na kontenerze ustawimy flex-wrap: wrap, to sprawimy wtedy, że elementy będą przechodziły do następnego wiersza w momencie, kiedy nie będą mieścić się już w main-axis kontenera.
Niestety - .navbar-list po przejściu do następnej linii nie jest już dociągnięty do prawej strony - innymi słowy, nie ma odstępu z lewej. Oznacza to, że justify-content: space-between; nie wystarcza do naszych potrzeb.
Ten sam efekt możemy osiągnąć przez ustawienie margin-left: auto; na .navbar-list. Wtedy element zawsze będzie miał margines automatyczny z lewej strony, niezależnie od tego czy flex-wrap już zadziałał, czy nie - a nasz znacznik zostanie dociągnięty do prawej.
OK, kolejny problem z głowy. Jednak wypadałoby jeszcze, aby .navbar po zawinięciu swoich dzieci do następnej linii troszkę się powiększył - w tym momencie elementy wyglądają na bardzo ściśnięte.
Ustawiamy min-height na 60px we wszystkich elementach w pasku nawigacyjnym. Wykorzystamy do tego wspoólny selektor .navbar, .navbar-list, .navbar-branding gdzie wydzielimy także powtarzające się display: flex;.
Super, ale oczywiście elementy nie są teraz wyrównane w pionie, ponieważ to .navbar ma align-items: center;, a pozostałe, które również są wyświetlane za pomocą flexa - nie.
Przenieśiemy więc align-items: center; do naszego wspólnego selektora dla wszystkich elementów w pasku nawigacyjnym:
Analogicznie do całego znacznika .navbar, ustalimy aby elementy wewnątrz .navbar-list zawijały się w momencie, gdy nie będą mieściły się już w kontenerze:
Następnie, ustawimy wspólny kolor tekstu dla tekstu w .navbar oraz odnosnikow a wewnątrz .navbar-list. Wykorzystamy do tego szary zapisany jako #a4a6ae w formacie szesnastkowym:
Aby dopasować się do designu, usuniemy też podkreślenie z naszych odnośników.
Nasz komponent powoli zaczyna nabierać kształtów. Przenieśmy wspólne style wyżej w pliku navbar.css, żeby móc łatwiej zarządzać naszym przykładem.
Chodzi o to, że gdybyśmy chcieli np. wykorzystać parę powtarząjących się reguł z selektorów ogólnych (mam tu na myśli te wymieniane po przecinku), ale nadpisać jedną lub dwie z nich na inne wartości, będzie to możliwe bez zabawy ze specificity.
Następnie, zajmijmy się wyglądem listy. Nadamy jej flex-grow: 1 aby "urosła", zajmując całą dostępną przestrzeń oraz justify-content: flex-end; by elementy były wyrównane do końca.
Dzięki temu, w momencie, kiedy lista zawinie się do następnej linii, dalej będzie zajmowała całą dostępną przestrzeń. Ot, dlaczego nie.
Chcielibyśmy, aby elementy wewnątrz .navbar-list miały między sobą pewne odstępy - oczywiście, możemy nadać każdemu elementowi li margines z lewej i z prawej.
Ustawimy więc margin: 0 10px; aby nadać margines zewnętrzny w osi poziomej oraz padding: 0 10px;, aby nadać margines wewnętrzny o tej samej wartości.
Dzięki temu będzie nieco łatwiej kliknąć w nasze odnośniki. Chwilowo włączyłem też klasę koduje-flex-tip na naszym elemencie .navbar.
Aby jeszcze bardziej uprościć klikalność elementów, dodamy .navbar-list a do pierwszego selektora, który ustawia nam minimalną wysokość elementu oraz pozycjonuje swoje dzieci w osi pionowej za pomocą flexa.
Super. Tak przy okazji - ustawmy 10 pikseli marginesu z prawej dla logo naszej strony w .navbar-branding.
Warto też zasygnalizować użytkownikowi na jakim elemencie znajduje się jego kuror myszy, poprzez zmiane koloru linków na #997fbc.
Osiągniemy to poprzez zmianę właściwośći color na selektorze .navbar-list a:hover.
Zobaczmy, czego jeszcze brakuje w naszym pasku nawigacyjnym. No, tak - wielkość liter w .navbar-branding.
Moglibyśmy zmienić po prostu tekst w kodzie HTML tak, by był napisany wielkimi literami - ale powinniśmy to zrobić za pomocą CSSa – pamiętajmy, że CSS odpowiada za wygląd, a HTML za semantykę. Nie ma semantycznie sensu, by były one pisane wielkimi literami.
Dzięki temu upewniamy się, że element będzie wyświetlał się tak jak tego oczekujemy, bez konieczności prawidłowego zapisu w markupie - jest to zabieg dekoracyjny, więc wykorzystamy właściwość text-trasnform: uppercase;
Poza tym musimy też dostosować odstęp pomiędzy literami w brandingu - no to siup - letter-spacing: 1.35px.
Jsteśmy już prawie u celu! Nadajmy border-bottom na naszym navbarze tak, aby lepiej oddzielał się od treści, które znajdą sie pod nim - z designu wyczytaliśmy, że powinien on mieć szerokość jednego piksela oraz kolor #d6d7dd:
Ostatni krok to poprawienie rozmiaru czcionki - wszystkie elementy w navbarze powinny miec font-size: 9px;
Super! Na ten moment zakończymy pracę nad naszym paskiem nawigacyjnym. Oczywiście możnaby wydzielić niektóre powtarzające się reguły oraz te, które akurat w tym konkretnym przypadku nic nie zmienią - to już ewentualne zadanie dla Ciebie :)
Sekcja z kartami
Przypomnijmy, jak wyglądała struktura sekcji z kartami:
Wykorzystamy część z reguł, które mieliśmy okazję stworzyć w poprzednich rozdziałach - przede wszystkim rozmiary, kolorystykę oraz ogólny wygląd kart.
Zwróć uwagę, że na .card-section nie nadaliśmy jeszcze display: flex;, dlatego elementy wyświetlają się jako normalne bloki.
Zajmijmy się tłem sekcji .card-section. Zaczniemy od ustawienia koloru na #f7f8fb oraz obrazka na img/bg-image.svg
Kolejnym krokiem będzie ustalenie rozmiaru tła na 150% szerokości i wysokości kontenera oraz jego wycentrowanie - dzięki temu uzyskamy efekt "przybliżenia" tła do środka:
No i oczywiście - display: flex; na .card-section sprawi, że elementy będą układały się jeden obok drugiego, a flex-wrap zapewni zawijanie elementów do kolejnych linii w przypadku nadmiarowych treści:
Jak wiemy, domyślną wartością dla justify-content jest flex-start - w związku z tym elementy są wyrównane do lewej, co nie wygląda zbyt dobrze. Zmieńmy więc wartość domyślną na space-around
Okej - elementy są już wyrównane w main-axis - teraz należy obsłużyć oś przeciwstawną. Ustawmy więc align-content na space-around; oraz align-items: center; aby obsłużyć oba przypadki - kiedy elementy nie są zawijane do następnych linii oraz wtedy, gdy zawijanie działa.
Pamiętaj też, że align-items nie ma wartości space-around.
No i świetnie! To było bardzo proste oraz szybkie w zakodowaniu. Możemy już usunąć klasę koduje-flex-tip z selektora .card-section.
Spróbujemy teraz wypozycjonować zawartość samej karty na flexie - tak dla sportu. Zaczniemy oczywiście od ustawienia display: flex; na selektorze .card. W tym momencie karty są zarówno dziećmi flexa jak i kontenerami.
Jeśli chcesz podejrzeć osie kart, najedź kursorem myszy na pierwszą kartę - w markupie nadałem jej odpowiednią klasę. Zoabczmy jak teraz prezentuje się nasz przykład:
Jak widzisz, elementy pozycjonują się domyślnie - czyli oś horyzontalna to main-axis. Chcielibyśmy, aby wyswietlaly się od góry do dołu, więc ustawimy dla każdej z kart flex-direction: column;. Przy okazji mozemy zmienic width na flex-basis.
Świetnie, wszystko wygląda tak, jak chcieliśmy, a pozycjonujemy to na flexie, co daje nam większą elastyczność.
Możemy też wycentrować elementy przy pomocy flexa zamiast uzywać text-align. W tym wypadku nie ma nic złego w użyciu text-align, natomiast ćwiczymy używanie flexboxa, prawda? :)
Ustawmy więc align-items na center na selektorze .card.
Jak widzisz, elementy zostały wycentrowane, ale niestety plakietka z kolorem karty została zwężona do szerokości cyfry.
Właściość align-items zadziałała horyzontalnie, ponieważ main-axis jest teraz osią pionowa, a cross-axis osią pozioma - dzieje sie tak za sprawą flex-direction: column.
Możemy naprawić ten problem, ustawiając na każdym selektorze .plate właściwość align-self na stretch - tak, by karta rozciągnęła się na całą dostępną szerokość.
Natomiast teraz numer każdej z kart jest przyciągnięty do lewej. Wycentrujmy go za pomocą flexa - nadajemy więc display: flex na selektorze .plate.
Jak widzisz nie ma żadnej różnicy, ponieważ domyślną wartoscią dla justify-content jest flex-start. Zmieńmy więc justify-content na center aby wyrównać identyfikator karty w main-axis oraz align-items na center aby wyrownac element w cross-axis.
Uff, puff... Udało się! Nie było to co prawda warte zachodu, ponieważ text-align: center na .card dawał nam dokładnie taki sam efekt, ale być może spojrzysz teraz na flexa z innej perspektywy. Nie jest to broń ostateczna dla każdego przypadku - oczywiście - da się, ale nie zawsze będzie to najlepsze rozwiązanie.
Nadeszła pora na stopkę naszej strony!
Stopka
To ostatnia część designu, który mieliśmy zakodować. Wygląda ona mniej więcej tak:
Nie ma na co czekać, do roboty!
Ukryjmy chwilowo całą resztę naszego kodu HTML - wybrane linijki nie są po prostu widoczne w edytorze, jednak dalej zobaczysz stworzone przez nas komponenty w podglądzie.
Stopka może być najbardziej skomplikowanym komponentem pod kątem markupu, dlatego rozbijemy go na mniesze fragmenty.
Zacznijmy od stworzenia znacznika footer o klasie .page-footer oraz nadajmy jej kolor tła na biały oraz wysokość na 160px.
Następnie stworzymy sekcję o klasie about-company. Sekcja składać się będzie z nagłówka trzeciego poziomu i paragrafu. Znajdzie się też tam nawigacja po kanałach społecznościowych.
Nadajmy więc wygląd naszej strukturze. Zaczniemy od zdefiniowania rozmiaru czcionki oraz koloru dla każdego nagłówka wewnątrz .page-footer - osiągniemy to za pomocą reguł color: #5a5c6b; oraz font-size: 10px;.
Usuńmy domyślne marginesy ze znaczników h3 i p wewnątrz .page-footer:
Świetnie. Ustawmy na znaczniku o klasie .about-company wyświetlanie za pomocą flexa oraz stałą wysokość 85px.
Ograniczymy szerokość wspomnianej sekcji do maksymalnie 200 pikseli poprzez regułę max-width: 200px; oraz zmienimy flex-direction na column, aby elementy były układane z góry na dół.
Ustawmy line-height, font-size oraz color akapitu wewnątrz .page-footer, aby spełnić wymagania designu.
W celu wyrównania elementów wewnątrz about-company ustawimy mu justify-content na space-between - chcemy przecież zarządzać przestrzenią, która jest dostepna w pionie - a oś pionowa to main-axis ze wzgledu na flex-direction: column;.
Wyłączymy text-decoration na odnośnikach wewnątrz sekcji .social-media, ustawiając text-decoration: none; - dzięki temu pozbędziemy się podkreślenia w odnośnikach na kanały społecznościowe.
Nadamy 20px marginesu wewnętrznego dla page-footer przy pomocy padding: 20px;:
Wypozycjonujemy teraz wszystkie elementy wewnątrz .page-footer na flexie, dzięki ustawieniu display: flex na kontenerze. Teraz możemy wyrównać je w pionie dzięki align-items:
Zanim przejdziemy do tworzenia kolejnych sekcji w stopce, zajmijmy się kolorowym obramowaniem. Aby stworzyć taki efekt, wykorzystamy pseudo-elementy, ponieważ bedzie to łatwiejsze niż zabawa z border-image. Oczywiście - dróg jest wiele – jeśli wolisz przetestować swoje rozwiązanie, to śmiało eksperymentuj z kodem wewnątrz edytora.
Stworzymy więc pseudo-element ::before na page-footer, a następnie nadamy mu pustą wartość poprzez content: ''; - tak, by mógł on zostać wyświetlony przez przeglądarkę.
Oczywiście, ustawimy mu też wysokość na 4px oraz szerokość rowną 100% swojego rodzica.
Jak widzisz, sekcja .about-company przesunęła się teraz w prawo. Zobaczmy jak wygląda nasz pseudo-element ::before nadając mu background na #5a5c6b - wtedy zobaczymy jego położenie na stronie.
Chcielibyśmy teraz, aby znajdował się on u góry kontenera page-footer. Naszej stopce nadamy position: relative, a na pseudo-elemencie ::before - position: absolute. Dzięki temu element ten będzie wypozycjonowany absolutnie wzgledem .page-footer.
Teraz pozostało wypozycjonować nasz element w lewym górnym rogu kontenera .page-footer.
Super! Mamy już nasze "specjalne obramowanie", musimy tylko zmienić jego kolor. Efekt widoczny w designie możemy osiągniąć poprzez wykorzystanie funkcji linear-gradient na background-image.
Funkcja ta jako pierwszy parametr przyjmuje kąt gradientu, który chcemy stworzyć - czyli jeśli chcielibyśmy zrobić ukośny gradient, możemy podać tam na przykład 45deg. My chcemy, aby wynosił on 90deg.
Następnie jako kolejne parametry podajemy kolory, z których chcemy stworzyć gradient. W naszym przypadku będą to #997fbc, #92ded2 oraz #fdc28a.
Usuwamy więc właściwość background z pseudo-elementu oraz ustawiamy mu następującą regułę: background-image: linear-gradient(90deg, #997fbc, #92ded2, #fdc28a);
Niestety - jak widzisz, obecnie otrzymaliśmy płynne przejście pomiędzy trzema podanymi przez nas kolorami - a przecież nie o to nam w tym przypadku chodzi.
Na szczęście możemy definiować punkty, w których konkretny kolor się kończy. Jest to tzw. color-stop. Dla każdego koloru możemy zdefiniować konkretny punkt, w którym ma on się skończyć, a kolejny kolor ma się zacząć.
Jak dokładnie działają gradienty opowiem w innym kursie, ponieważ to dosyć spore zagadnienie - natomiast w dużym skrócie, taki zapis: linear-gradient(90deg, #997fbc 40%, #92ded2 40%, #92ded2 80%, #fdc28a 80%); pozwoli nam osiągnąć dokładnie to, o co nam chodzi.
Kolejne sekcje będą będą wyglądały całkiem podobnie, więc użyjmy jednej wspólnej klasy footer-list.
Sekcja zatytułowana w designie jako Menu będzie to znacznik nav z nagłówkiem o takiej samej treści. Będzie miał w sobie listę nienumerowaną ul zawierającą linki do poszczególnych sekcji, tak jak w liście z górnego menu nawigacyjnego na naszej stronie.
Postaram się też nieco przyśpieszyć z przejściami do kolejnych snippetów kodu :)
Chcielibyśmy, aby każda z list miała 100px szerokości oraz wysokość 85px - czyli równą wysokości sekcji .about-company. Tworzymy więc odpowiedni selektor .footer-listi definiujemy interesujące nas reguły:
Chcielibyśmy też, aby lista nienumerowana z linkami wyglądała analogicznie do paragrafu w sekcji .about-company. Na początek usuniemy domyślne marginesy wewnętrzne i zewnętrzne z .footer-list ul(można to oczywiście wydzielić do innego ogólnego selektora :) ):
Dodamy też .footer-list a do selektorów .social-media a oraz .page-footer p
Następnie nadamy margin-bottom: 15px do każdego h3 znajdującego się w stopce za pomocą selektora .page-footer h3 i uporządkujemy nieco nasze reguły.
Aby struktura kodu była bardziej uniwersalna i niezależna od użytych znaczników, stworzymy klasy .footer-wrapper oraz .footer-item.
Klasę .footer-wrapper wypozycjonujemy na flexie w ten sposób, że main-axis będzie osią pionową, a elementy wewnątrz niej będą rozmieszczone tak, że przestrzeń zostanie rozdystrybuowana między nimi.
Musimy użyć flex-direction: column; oraz justify-content: space-between;.
Analogiczne reguły ustawimy dla całego .footer-list. Pozostało tylko nadać klasę .flex-wrapper dla naszej listy .footer-list ul.
Ustalmy, aby .flex-wrapper zajmował całą dostępną przestrzeń za pomocą flex-grow: 1;.
Teraz powinniśmy nadać klasę .footer-item dla każdego elementu, który będzie pokazywać sie poniżej nagłówka wewnątrz .footer-list, dzięki temu nie musimy zwracać uwagi na typ danego znacznika.
Ustawmy wszystkie właściwości, które chcemy współdzielić pomiędzy elementami .footer-list na .footer-item.
Ustawmy line-height, font-size, height, margin-bottom oraz color na interesujące nas wartości.
W końcu, ustawmy stworzoną klasę na wszystkich li:
Teraz wystarczy stworzyć odpowiednią strukturę, reprezentującą sekcję zatytułowaną jako Adres w designie, np w ten sposób:
<address class="footer-list">
<h3>
Adres
</h3>
<div class="footer-wrapper">
<span class="footer-item">
Nazwa Firmy
</span>
<span class="footer-item">
ul. Przemysłowa 254
</span>
<span class="footer-item">
00-204 Warszawa
</span>
</div>
</address>
Oczywiście, podany kod mógłby być pełniejszy semantycznie poprzez użycie innych znaczników czy atrybutów aria, ale nie to jest tematem naszych ćwiczeń.
Jak widzisz, znacznik address narzuca nam font-style: italic; na swoich dzieciach. Dlatego ręcznie dopiszemy font-style: normal; na naszym elemencie .footer-list:
Super! Teraz wszystko wyświetla się poprawnie. Pozostało dodać ostatnią sekcję zatytułowaną Kontakt:
<section class="footer-list">
<h3>
Kontakt
</h3>
<div class="footer-wrapper">
<span class="footer-item">
Telefon: 0234 2415 1231
</span>
<span class="footer-item">
E-mail: hello@email.com
</span>
</div>
</section>
Wypozycjonujmy elementy wewnątrz .page-footer za pomocą flexa - ustawimy justify-content na flex-end:
Oraz ustawimy margin-right: auto na about-company aby wypchnąć dany element do lewej strony (oczywiście moglibyśmy też wykorzystać align-self).
Przejdźmy do zawijania elementów wewnątrz .page-footer poprzez ustawienie flex-wrap: wrap:
Jak widzisz, niestety nie zawija się to najlepiej. Poprawimy nieco tę sytuację poprzez zmianę height: 160px; z .page-footer na min-height:
Teraz elementy są zawsze na białym tle, zapewnionym przez .page-footer. Co dalej - nadajmy każdemu z dzieci równą wysokość oraz margines od dołu równy 20px.
Możemy też usunąć niepotrzebne reguły z selektorów powyżej, takie jak height z .footer-list i .about-company.
Usuńmy regułę justify-content: space-between; z 72 linijki aby elementy .footer-list pozycjonowały się domyślnie - czyli na flex-start.
Dlaczego? T elementy listy mają przestrzeń pomiędzy sobą - co nie wyglada dobrze, jeśli mamy różną liczbę elementów w kilku .footer-list.
Jak widzisz, elementy są zawijane do kolejnych linii jeśli nie starczy na nie miejsca. Dla bezpieczeństwa, zmieńmy height: 120px na min-height.
Przy okazji - sekcja .about-company ma akapit troszkę niżej, niż elementy w .footer-list. Poprawmy więc jego justify-content zmieniając space-around na flex-start. Wystarczy, że usuniemy tą linijkę jako, że jest to domyślna wartość justify-content.
Nie jest to może najlepszy sposób tzw. składania się tego layoutu, ale bez większego problemu możemy go osiągnąć - nawet bez użycia ani jednego media-query. Jeśli chcesz możesz stworzyć wrapper na wszystkie elementy .footer-list i zarządzać zawijaniem wewnątrz tego nowoutworzonego elementu.
W tym momencie skończymy ten niesamowicie długi rozdział.
W ostatnim przypomnę wszystkie właściwosci CSS Flexbox, które mieliśmy okazje poznać, przygotuję dla Ciebie edytor, w którym będziesz mógł zakodować przedstawiony design od zera, oraz wskażę Ci, które fragmenty kodu można spokojnie zrobić lepiej niż w przedstawionym przeze mnie przykładzie.