Blog

Hugo Tutorial PL. Odsłona czwarta.

Kontynuacja pracy z szablonami, ale tym razem na poziomie "hard". A tak serio nieco głębiej wchodzimy w temat z pożytkiem dla łatwości przyszłego rozwijania naszej witryny.

Hugo Tutorial PL. Odsłona czwarta.

Na koniec dzisiejszej lekcji będziemy mieli gotowy szablon pierwszej strony i w sumie tak naprawdę znali wszystkie narzędzia (a przynajmniej ich absolutne minimum), by przygotować pozostałe.

Zanim jednak przejdziemy do części praktycznej skopiujmy wprzódy fotki, arkusze CSS oraz skrypty JS z pobranego wcześniej tematu Clean Blog do naszego projektu, a dokładniej do katalogu /themes/cb-one/static. W efekcie folder ten powinien prezentować się następująco:

├── assets

│   ├── favicon.ico

│   └── img

│       ├── about-bg.jpg

│       ├── contact-bg.jpg

│       ├── home-bg.jpg

│       ├── post-bg.jpg

│       └── post-sample-image.jpg

├── css

│   └── styles.css

└── js

└── scripts.js

Po tym wszystkim szybko przechodzimy do rzeczy.

Wspominałem już o tym przy kilku okazjach, ale może warto takie rzeczy co jakiś czas powtarzać: cała idea CMS-ów czy SSG opiera się na budowie “modułowej” naszej witryny, a przynajmniej wtedy dopiero mają te rozwiązania największy sens, tak od strony praktycznej jak i estetycznej, ponieważ między innymi w ten sposób najprościej utrzymać pewną wizualną spójność dla wszystkich stron składających się na naszą witrynę. Choć oczywiście czasem może zachodzić potrzeba wyróżnienia którejś z nich w jakiś zdecydowany sposób, ale na ogół zależy nam na zachowaniu w miarę jednolitego charakteru całej witryny, a przynajmniej pewnych je sekcji.

Oczywiście można się spierać, czy faktycznie taka homogenizacja podnosi wartość estetyczną naszego portalu, to jednak raczej poza dyskusją jest, że takie podejście ułatwia poruszanie się po stronach naszej witryny, ponieważ pewne typowe i ważne ich elementy znajdują się zawsze w tym samym miejscu i wyglądają w sposób zbliżony, jeśli nie identyczny (na przykład nawigacja czy przycisk wyszukiwania). Nieprzypadkowo większość witryn w swoim układzie jest bardzo podobna, bo w niemałej części tego oczekują użytkownicy. Dlatego też przeglądając najpopularniejsze portale łatwo jest wyróżnić pewne stałe komponenty na każdej ze stron, które ułatwiają poruszanie się po nich.

Takie najczęściej spotykane składowe to - idąc od góry - nawigacja czy cały nagłówek, czasem jakiś panel boczny czy nawet dwa (sidebar) oraz stopka (footer). Właśnie z tego powodu bardzo lubię do takich wprawek jak nasza wykorzystywać temat Clean Blog, ponieważ przy całej swojej prostocie zawiera te podstawowe elementy, które są do tego bardzo wyraźnie odseparowane od siebie.

Tym niemniej by w ogóle myśleć o jakimś sensownym poszatkowaniu stron dla naszej witryny na mniejsze elementy, musimy posiadać podstawową matrycę, która będzie wskazywała na te fundamentalne elementy. Tutaj z pomocą przychodzą nam autorzy Hugo, gdyż przy kreowaniu naszego tematu za pomocą CLI w katalogu _default został już utworzony plik o niespecjalnie zgrabnej nazwie baseof.html (jakby samo “base.html” nie wystarczyło).

Przejdźmy więc do tego folderu (ścieżka: themes/cb-one/layout/_default) i otwórzmy rzeczony plik. Wówczas naszym oczom powinien ukazać się bazowy szkielet strony, którą możemy wykorzystać przy budowie motywu.

<!DOCTYPE html>
<html>
   {{- partial "head.html" . -}}
   <body>
       {{- partial "header.html" . -}}
       <div id="content">
       {{- block "main" . }}{{- end }}
       </div>
       {{- partial "footer.html" . -}}
   </body>
</html>

Jak widać praktycznie nie ma tu kodu HTML, jest za to całkiem sporo odwołań do innych plików, które widzimy między nawiasami klamrowymi. Rozszyfrowanie tego “zapisu” nie powinno nastręczyć większego trudu osobie, która wcześniej zapoznała się ze strukturą przygotowanego tematu. Otóż słowa “partial” wskazuje na pewien istniejący katalog, zaś między cudzysłowami znajduje się nazwa pliku, który w tym folderze powinien się znajdować. Zawartość tego pliku będzie po prostu wstrzykiwana w to miejsce przy generowaniu strony.

Jest jednak jeden wiersz, który nie wpisuje się w ten schemat  {{- block "main" . }}{{- end }}, ale nim się zajmiemy w swoim czasie. Póki co spreparujmy te trzy pozostałe składowe, czyli head, header oraz footer. W tym celu potniemy odpowiednio zawartość pliku index.html, który znajduje się w katalogu z motywem Clean Blog. W efekcie do pliku head.html z folderu partial dorzucimy następujący kawałek kodu:

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Clean Blog - Start Bootstrap Theme</title>
<link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
<!-- Font Awesome icons (free version)-->
<script src="https://use.fontawesome.com/releases/v5.15.3/js/all.js" crossorigin="anonymous"></script>
<!-- Google fonts-->
<link href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="css/styles.css" rel="stylesheet" />
</head>

Do pliku header.html poleci nawigacja oraz nagłówek strony:

<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-light" id="mainNav">
<div class="container px-4 px-lg-5">
<a class="navbar-brand" href="index.html">Start Bootstrap</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="fas fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto py-4 py-lg-0">
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="index.html">Home</a></li>
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="about.html">About</a></li>
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="post.html">Sample Post</a></li>
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="contact.html">Contact</a></li>
</ul>
</div>
</div>
</nav>
<!-- Page Header-->
<header class="masthead" style="background-image: url('assets/img/home-bg.jpg')">
<div class="container position-relative px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
<div class="site-heading">
<h1>Clean Blog</h1>
<span class="subheading">A Blog Theme by Start Bootstrap</span>
</div>
</div>
</div>
</div>
</header>

Zaś do footer.html pójdzie cała stopka oraz umieszone na końca odwołania do skryptów JS:

<!-- Footer-->
<footer class="border-top">
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
<ul class="list-inline text-center">
<li class="list-inline-item">
<a href="#!">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#!">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li class="list-inline-item">
<a href="#!">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<div class="small text-center text-muted fst-italic">Copyright &copy; Your Website 2021</div>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script src="js/scripts.js"></script>

Pora teraz wyjaśnić o co chodzi z tym blokiem main, o którym była wyżej mowa przy okazji oglądania zawartości pliku beseof.html? Jeśli mam być szczery, to nie potrafię odpowiedzieć na to pytanie, ponieważ w dokumentacji Hugo nie znalazłem żadnego wyjaśnienia jego roli czy genezy tej konstrukcji. Na bazie tego jak on jest używany w procesie tworzenia motywów domyślam się, że “silnik” Hugo dzięki takiemu oflagowani wie, że ta część strony/szablonu wymaga nieco innej logiki generowania niż proste odwołanie się do jakiegoś konkretnego pliku znajdującego się w konkretnym folderze, tak jak w przypadku trzech wcześniejszych elementów.

Otwórzmy teraz plik index.html w folderze themes/cb-one/layout/. W tym momencie jest on zupełnie pusty, więc szybko zmienimy ten stan rzeczy dodając w jego środku następujący kod:

{{ define "main" }}
<!-- Main Content-->
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
​           {{ .Content }}
</div>
</div>
</div>
{{ end }}

Kluczowe w tym wypadku jest wyrażenie w nawiasach, ponieważ jest to informacja, że Hugo powinien pobrać zawartość (treść), która została udostępniona w ramach odpowiedniego pliku Markdawn, a ponieważ jest to szablon, który ma być zastosowany w przypadku strony głównej (homepage), to będzie chodziło o plik _index.md z folderu /content. Dlatego otwórzmy go na chwilę i pod przerywanymi liniami dodajmy następujący fragment, by całość wyglądała następująco:

---
title: "Home"
date: 2021-07-27T16:31:45+02:00
draft: false
---
# Witaj na moim blogu

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam risus elit, accumsan et consectetur a, gravida et ante. Pellentesque pharetra non mi in commodo. Morbi ullamcorper molestie malesuada. Donec vel tincidunt eros, in auctor tortor. Vivamus facilisis enim ac dolor hendrerit, vel porta eros lacinia. Mauris dictum dignissim orci, vitae consequat enim finibus id. Sed eros est, maximus quis enim a, venenatis accumsan massa. Nullam vulputate nibh at nisi elementum, at rhoncus turpis imperdiet. Nam auctor purus id diam porta ullamcorper. Aliquam volutpat mollis fringilla. Quisque sodales porttitor nisl, in euismod orci pharetra a.
Proin accumsan leo id aliquet mattis. Proin tristique risus vel ante iaculis, vel sagittis turpis luctus. Maecenas suscipit a tortor ut mollis. Donec elementum, metus vel viverra laoreet, turpis tellus porta nibh, congue rhoncus tellus arcu sit amet lacus. Maecenas tincidunt tincidunt magna. Phasellus cursus nisi tortor, eu vestibulum erat molestie a. Nunc sollicitudin imperdiet augue vitae vestibulum. Mauris non aliquam lacus, pellentesque dictum justo. Quisque mattis dictum maximus. Pellentesque sodales libero ac lectus vehicula, vitae rhoncus velit facilisis. Phasellus id iaculis lectus. Sed elementum consectetur ipsum, ac semper arcu ornare ac. Fusce faucibus enim lobortis augue finibus interdum. Pellentesque ultrices facilisis metus a molestie.

W tym momencie możemy wreszcie opalić serwer Hugo polecenie $ hugo server i cieszyć oczy pierwszą z naszych stron.

Skoro nam tak dobrze poszło, to na koniec załatwić spróbujemy temat z wyświetleniem innych, czyli pojedynczych (simple) stron. W tym celu dodajmy treść w pliku index.md umieszczonym w katalogu content/about/, tak by wyglądała jakoś tak:

---
title: "About"
date: 2021-07-27T16:32:03+02:00
draft: false
---
# O moim blogu
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam risus elit, accumsan et consectetur a, gravida et ante. Pellentesque pharetra non mi in commodo. Morbi ullamcorper molestie malesuada. Donec vel tincidunt eros, in auctor tortor. Vivamus facilisis enim ac dolor hendrerit, vel porta eros lacinia. Mauris dictum dignissim orci, vitae consequat enim finibus id. Sed eros est, maximus quis enim a, venenatis accumsan massa. Nullam vulputate nibh at nisi elementum, at rhoncus turpis imperdiet. Nam auctor purus id diam porta ullamcorper. Aliquam volutpat mollis fringilla. Quisque sodales porttitor nisl, in euismod orci pharetra a.

Następnie otwórzmy w edytorze plik single.html posadowiony w katalogu /themes/layout/_default/ i dodajemy dokładnie ten sam kod, co dla pliku index.html, czyli:

{{ define "main" }}
<!-- Main Content-->
<div class="container px-4 px-lg-5">
<div class="row gx-4 gx-lg-5 justify-content-center">
<div class="col-md-10 col-lg-8 col-xl-7">
          {{ .Content }}
</div>
</div>
</div>
{{ end }}

Powinno to sprawić, że tym razem ujrzymy stronę /about po odpaleniu servera. Niestety od razu będziemy mogli się przekonać, że przy próbie przejścia do niej przy pomocy menu z prawego górnego rogu, w efekcie dostaniemy informację o braku możliwości jej odnalezienia, a jeśli wpiszemy z palca http://localhost:1313/about/ strona się wyświetli, ale bez formatowania czy tam stylizacji. Zajmijmy się po koeli tymi dwoma problemami.

Problem z menu bierze się z faktu, że w gotowym temacie mamy odwołanie do nazw plików zawierających rozszerzenie .html, czyli wybierając przycisk about odwołujemy się do strony pod adresem http://localhost:1313/about.html/, czyli jednak innej niż ta wyżej. By to “naprawić” musimy otworzyć plik header.html z katalogu partial i poprawimy fragment kodu dotyczący nawigacji właśnie, by na końcu otrzymać coś takiego:

<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto py-4 py-lg-0">
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="about">About</a></li>
<li class="nav-item"><a class="nav-link px-lg-3 py-3 py-lg-4" href="blog">Blog</a></li>
</ul>
</div>

Jak widać wyleciały wiersze dotyczące Contact oraz Sample Post, ale za to pojawił się nowy dotyczący Bloga. Skoro już tutaj jesteśmy to poprawmy również odwołanie przypisane do napisu Start Bootstrap. W tej chwili wygląda ono tak:

<a class="navbar-brand" href="index.html">Start Bootstrap</a>

By ten odnośnik zaczął spełniać swoją funkcję musimy usunąć "index.html" i w to miejsce wstawić “/”. Po tych modyfikacjach zapisujemy plik i teraz już powinno menu w jakimś tam zakresie działać (przycisk “Blog” cały czas będzie rzucał błędem).

Niestety strona “About” wygląda jak wygląda, a to ze względu na fakt, że de facto nie są wczytywane arkusze stylów CSS. By to naprawić wystarczy trochę podrasować odwołania w pliku head.html. Otwieramy go więc w naszym edytorze i poprawiamy w dwóch rekordach linki, tak by wyglądały jak niżej:

<link rel="icon" type="image/x-icon" href={{ "assets/favicon.ico" | relURL}} />
…
<link href={{ "css/styles.css" | relURL}} rel="stylesheet" />

Jak już te odwołania poprawiamy to zróbmy to samo dla skryptów JS, które są w pliku footer.html, czyli modyfikujemy ostatni wiersz, by wygląda tak jak niżej:

<script src={{"js/scripts.js" | relURL}}></script>

Tym razem strona powinna wyglądać znacznie lepiej, ale nie do końca z naszymi oczekiwaniami, bo nie dość, że nie widzimy żadnego zdjęcia znajdującego się w tle naszego nagłówka, a i napisy tam umieszczone są identyczne jak na stronie głównej. Ponieważ poprawienie tego stanu rzeczy wymagać będzie wprowadzenie kolejnego zagadnienia z zakresu architektury projektów w Hugo zostawimy to sobie na kolejny wpis.