author Ahmad Muhardian

Membuat Layout Tema Hugo: 3 Halaman Penting yang Harus Ada dalam Blog


Setelah kita membuat template baru, hal yang harus dilakukan selanjutnya adalah membuat layout untuk halaman-halamannya.

Adapun halaman-halaman yang harus ada di dalam sebuah blog adalah:

  1. Homepage/landing page (index.html)
  2. Halaman Artikel (single.html)
  3. Halaman Taxonomy atau daftar artikel pada kategori atau tag tertentu (list.html)

Namun sebelum membuatnya, kita harus membuat partial terlebih dahulu.

Partial adalah pecahan-pecahan kode atau komponen dari template.

Adanya partial memungkinkan kita untuk menggunakan ulang komponen yang sudah ada.

(Catatan: Pembuatan template ini menggunakan Hugo versi 0.26. Silakan melakukan upgrade, buat yang belum upgrade)

Persiapan Bahan dan Bumbu-bumbunya

Adapun bahan-bahan dan bumbu yang akan kita gunakan dalam pembuatan template ini adalah sebagai berikut:

  1. Bootstrap4: Meskipun masih versi Beta, saya rasa sudah cukup stabil.
  2. Prismjs: Untuk syntax highlighting di artikel (blog programmer diharuskan pakai ini 😄)

Bisakah saya pakai yang lain?

Ya bisa, asalkan masih bisa mengikuti tutorial ini.

Dua bumbu di atas saya rekomendasikan karena:

Baik, untuk memudahkan silakan download semua bumbu-bumbu tersebut di respository ini: https://github.com/petanikode/bumbu-template

Download Bahan dan Bumbu

Setelah itu, ekstrak atau copy isinya ke dalam direktori static di dalam temanya.

Ekstrak ke Tema

Atau cara cepat, bisa melalui Terminal:

# pindah ke direktori static
cd themes/[nama-tema]/static

# clone/download dari repository
git clone https://github.com/petanikode/bumbu-template.git

# hapus database git
rm -rf .git

Membuat Partial

Ada beberapa partial yang harus kita buat:

  1. head.html untuk isi di dalam tag <head>;
  2. header.html untuk header blog;
  3. footer.html untuk footer;
  4. post-grid.html untuk menampilkan artikel dengan card view;
  5. aside.html untuk container widget atau sidebar;
  6. js-init.html untuk inisialisasi javascript;
  7. nav.html untuk navbar;
  8. disqus.html untuk komentar Disqus.
  9. pagination.html untuk navigasi pagination
  10. dan lain-lain jika ingin ditambahkan lagi.

Untuk lebih detailnya, mari kita buat satu-per-satu…

atau kalau tidak mau repot bisa membuat semua filenya dengan perintah berikut:

# pindah ke direktori partial
cd themes/[nama-tema]/partials
# buat file kosong
touch head.html header.html footer.html post-grid.html aside.html js-init.html nav.html disqus.html pagination.html

Setelah itu tinggal diisi kodenya saja 😄.

1. Membuat partials/head.html

Partial ini isinya adalah bagian yang ada di dalam tag <head>.

Biasanya di dalamnya ada tag <meta>, <link> untuk css dan ikon, dan lain-lain.

Untuk mengetahui kode apa saja yang ada di dalam tag <head>, saya rekomendasikan untuk melihat-lihat repositori ini: HEAD.

Berikut ini isi partial head.html:

<title>{{ .Title }}</title>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
{{ template "_internal/schema.html" . }}
{{ template "_internal/google_news.html" . }}
{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}


<!-- Icon -->
<link rel="icon" href="/img/logo.svg" />

<link rel="stylesheet" href="/css/bootstrap.min.css" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/prism.css" />

<!-- Taru kode Google Analytic di sini, karena menurut aturan harus di dalam Head -->
{{ template "_internal/google_analytics.html" . }}

Penjelasan:

{{ .Title }} akan mencetak title dari blog dan artikel. Tergantung konteksnya, apabila partial ini di-include di homepage, maka akan ditampilkan judul web atau blog.

Tapi kalau di-include di dalam single.html, maka akan ditampilkan judul artikel.

Berikutnya kode-kode ini:

{{ template "_internal/schema.html" . }}
{{ template "_internal/google_news.html" . }}
{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}

Mereka adalah partial bawaan Hugo untuk SEO.

Enak bukan, kita tidak perlu repot-repot membuatnya. Tinggal di-include saja.

Kalau masih kurang, bisa ditambahkan lagi.

2. Membuat partials/header.html

Partial ini berisi kode untuk bagian header:

<header>
    <div class="jumbotron jumbotron-fluid bg-success text-white">
        <div class="container">
            <div class="row">
                <div class="col">
                    <h1>Programmer Pengguna Linux</h1>
                    <p class="lead">Belajar Pemrograman Apapun Menggunakan Linux</p>
                </div>
            </div>
        </div>
    </div>
</header>

Hasilnya setelah di-include:

Partial Header

3. Membuat partials/footer.html

<footer class="border border-bottom-0 border-left-0 border-right-0">
    <div class="container pt-3 pb-3 pt-md-5 pb-md-5">
        <div class="row">
            <div class="col">

                &copy; {{ now.Format "2006"}} <a href="{{.Site.BaseURL}}">{{ .Site.Title }}</a> | Template by <a href="https://www.petanikode.com" target="blank">Petani Kode</a>

            </div>
        </div>
    </div>
</footer>

Penjelasan:

  • {{ now.Format "2006" }} akan menghasilkan tahun sekarang;
  • {{ .Site.BaseURL }} akan menghasilkan base URL sesuai konfigurasi;
  • {{ .Site.Title }} akan menghasilkan judul web/blog meskipun di-include di single.html, karena mengambil dari .Site.

4. Membuat partials/post-grid.html

Partial ini untuk menampilkan daftar artikel dalam bentuk card view serta navigasi pagination-nya:

<article class="card-post">
    <div class="container">
        <div class="row">

            {{ $paginator := .Paginate (where .Site.Pages "Type" "post") 6 }}
            {{ range $paginator.Pages }}
                <div class="col-md-4 mb-4 d-flex">
                    <div class="card">
                        <a href="{{ .RelPermalink }}">
                        <img class='card-img-top' 
                            src='{{ if eq .Params.Image "" }}https://placeholder.pics/svg/320x160{{else}}{{ .Params.Image}}{{end}}'
                            alt='{{ .Title }}'>
                        </a>
                        <div class="card-body">
                            <h4 class="card-title">
                                <a class="text-dark" href="{{ .RelPermalink }}">{{ .Title }}</a>
                            </h4>
                        </div>
                    </div>

                </div>
            {{ end }}

        </div>
    </div>
</article>

<br>

<div class="container">
    {{ partial "pagination.html" . }}
</div>

Penjelasan:

Pertama kita mengambil halaman dengan type "post" sebanyak 6, lalu disimpan ke dalam variabel $paginator.

{{ $paginator := .Paginate (where .Site.Pages "Type" "post") 6 }}

Sekarang isi variabel $paginator adalah sebuah array dari objek artikel.

Setelah itu, kita bisa melakukan perulangan dengan perintah range.

{{ range $paginator.Pages }}
    <div class="col-md-4 mb-4 d-flex">
        <div class="card">
            <a href="{{ .RelPermalink }}">
                <img class='card-img-top' 
                    src='{{ if eq .Params.Image "" }}https://placeholder.pics/svg/320x160{{else}}{{ .Params.Image}}{{end}}'
                    alt='{{ .Title }}' />
            </a>
            <div class="card-body">
                <h4 class="card-title">
                    <a class="text-dark" href="{{ .RelPermalink }}">{{ .Title }}</a>
                </h4>
            </div>
        </div>
    </div>
{{ end }}

Penjelasan:

  • {{ range $paginator.Pages }}, artinya lakukan perulangan sebanyak isi $paginator;
  • {{ .RelPermalink }} akan mencetak permalink (permanent link) dari artikel;
  • {{ if eq .Params.Image "" }} artinya kalau front-matter Image kosong, maka kita isi dengan placeholder dari placeholder.pics. Tapi kalau ada isi, maka isi dengan url gambar tersebut ({{ .Params.Image }});
  • {{ .Title }} tampilkan judul artikel;
  • {{ end }} akhiri blok perulangan.

Hasilnya:

Card View bootstrap 4

5. Membuat partials/aside.html

Partial ini untuk menampung widget bagian samping (sidebar).

<aside>

<div class="widget">
    <a href="http://projects.id/petani_kode" target="_blank">
    <img src="http://lorempixel.com/360/250" alt="Proyek Petani Kode"/>
    </a>
</div>

</aside>

Tips:

Saya biasanya membuat direktori widget di dalam direktori partial untuk menampung widget-widget yang akan digunakan.

Direktori widget:

layouts/partials/
├── aside.html
├── disqus.html
├── footer.html
├── header.html
├── head.html
├── js-init.html
├── nav.html
├── post-gird.html
└── widget
    ├── adsense-300x250.html
    └── newsletter-form.html

Lalu untuk menggunakannya di partial aside.html, tinggal di-include.

<aside>
    {{ partial "widget/newsletter-form.html" . }}
    {{ partial "widget/adsense-300x250.html" . }} 
</aside>

dan untuk menon-aktifkannya, tinggal dikomentari:

<aside>
    <!-- {{ partial "widget/newsletter-form.html" . }} -->
    {{ partial "widget/adsense-300x250.html" . }} 
</aside>

6. Membuat partials/js-init.html

Seperti namanya, partial ini tempat meng-include dan menulis kode-kode javascript yang diperlukan.

Berikut kodenya:

<script type="text/javascript" src="/js/jquery-3.2.1.slim.min.js"></script>
<script type="text/javascript" src="/js/popper.min.js"></script>
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/js/prism.js"></script>

Kenapa tidak ada css-init?

Ya karena css sudah kita include di dalam partial head.html.

Sedangkan partial js-init.html akan kita include dibagian akhir halaman.

Karena menurut beberapa kepercayaan:

Meng-include atau menulis kode javascript di bagian akhir dokumen HTML dapat mempercepat Web.

7. Membuat partials/nav.html

Partial ini untuk membuat menu navigasi, silakan diubah sesuai kebutuhan:

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container">

        <a class="navbar-brand" href="{{.Site.BaseURL}}"><img src="/img/logo.svg" width="30" height="30" class="d-inline-block align-top rounded-circle"
            alt=""> {{ .Site.Title }}</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
            aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="{{.Site.BaseURL}}">Home <span class="sr-only">(current)</span></a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="about.html">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="contact.html">Contact</a>
                </li>
            </ul>
            <form class="form-inline my-2 my-lg-0" action="https://www.google.co.id" id="cse-search-box">
                <input type="hidden" name="cx" value="partner-pub-6279325630224392:9670052267" />
                <input type="hidden" name="ie" value="UTF-8" />
                <input type="hidden" name="sa" value="search" />
                <input class="form-control mr-sm-2" type="text" name="q" placeholder="Kata Kunci" aria-label="Search">
                <button class="btn btn-primary my-2 my-sm-0" type="submit">Cari</button>
            </form>
        </div>
    </div>
</nav>

8. Membuat partials/disqus.html

Partial ini untuk menampilkan komentar Disqus yang bisa kita include di bawah artikel.

<div id="disqus_thread"></div>
<script type="text/javascript">
    (function () {
        // Don't ever inject Disqus on localhost--it creates unwanted
        // discussions from 'localhost:1313' on your Disqus account...
        if (window.location.hostname == "localhost")
            return;

        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
        var disqus_shortname = '{{ .Site.DisqusShortname }}';
        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
    })();

</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com/" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>

Penjelasan:

  • Kode disqus ini hanya akan tampil setelah deploy, sedangkan kalau di localhost tidak akan tampil. Hal ini untuk mencegah pembuatan link baru di akun disqus kita.
  • Variabel {{ .Site.DisqusShortname }} akan kita buat di config.toml

9. Membuat partials/pagination.html

Partial ini untuk membuat link navigasi pagination.

Saya kurang paham kodenya, karena ngambil (copas) dari template internal Hugo 😄 dan sedikit modifikasi agar mendukung untuk Bootstrap 4.

<nav aria-label="Page navigation">
{{ $pag := $.Paginator }}
{{ if gt $pag.TotalPages 1 }}
<ul class="pagination">
    {{ with $pag.First }}
    <li class="page-item">
        <a class="page-link" href="{{ .URL }}" aria-label="First"><span aria-hidden="true">&laquo;&laquo;</span></a>
    </li>
    {{ end }}
    <li
    {{ if not $pag.HasPrev }}class="disabled page-item"{{ end }}>
    <a class="page-link" href="{{ if $pag.HasPrev }}{{ $pag.Prev.URL }}{{ end }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>
    </li>
    {{ $.Scratch.Set "__paginator.ellipsed" false }}
    {{ range $pag.Pagers }}
    {{ $right := sub .TotalPages .PageNumber }}
    {{ $showNumber := or (le .PageNumber 3) (eq $right 0) }}
    {{ $showNumber := or $showNumber (and (gt .PageNumber (sub $pag.PageNumber 2)) (lt .PageNumber (add $pag.PageNumber 2)))  }}
    {{ if $showNumber }}
        {{ $.Scratch.Set "__paginator.ellipsed" false }}
        {{ $.Scratch.Set "__paginator.shouldEllipse" false }}
    {{ else }}
        {{ $.Scratch.Set "__paginator.shouldEllipse" (not ($.Scratch.Get "__paginator.ellipsed") ) }}
        {{ $.Scratch.Set "__paginator.ellipsed" true }}
    {{ end }}
    {{ if $showNumber }}
    <li
    {{ if eq . $pag }}class="active page-item"{{ end }}><a class="page-link" href="{{ .URL }}">{{ .PageNumber }}</a></li>
    {{ else if ($.Scratch.Get "__paginator.shouldEllipse") }}
    <li class="disabled page-item"><a class="page-link" href="#"><span aria-hidden="true">&hellip;</span></a></li>
    {{ end }}
    {{ end }}
    <li
    {{ if not $pag.HasNext }}class="disabled page-item"{{ end }}>
    <a class="page-link" href="{{ if $pag.HasNext }}{{ $pag.Next.URL }}{{ end }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>
    </li>
    {{ with $pag.Last }}
    <li class="page-item">
        <a class="page-link" href="{{ .URL }}" aria-label="Last"><span aria-hidden="true">&raquo;&raquo;</span></a>
    </li>
    {{ end }}
</ul>
{{ end }}
</nav>

Membuat Konfigurasi dan Archetype

Sebelum mulai membuat halaman, silakan buat dulu konfigurasi dan archetype yang dibutuhkan, agar tidak error saat mencoba temanya.

Adapun konfigurasi yang kita butuhkan adalah sebagai berikut (config.toml).

title = "Petani Kode"
baseurl = "https://www.petanikode.com/"

# Agar front-matter pakai format YAML
MetaDataFormat = "yaml"

# untuk apply temanya
themesDir = "themes"
theme = "kacang"

# Shortname disqus, silakan sesuaikan
DisqusShortname = "petanikode"

# Untuk Google Analytic, silakan sesuaikan
googleAnalytics = "UA-80517197-1" 

# Untuk deskripsi tag meta di homepage
description = "Blog Programmer pengguna Linux"

# Biodata Author
[author]
    name = "Ardianta Pargo"
    homepage = "https://twitter.com/ardiantapargo"
    bio = "Pengamat Langit"
    image = "https://lh5.googleusercontent.com/-h2tLsyijw8Q/AAAAAAAAAAI/AAAAAAAACys/WBpjN_34z3o/s32-c/photo.jpg"

Selanjutnya silakan tambahkan archetype default.md, buat direktori dan file baru:

cd themes/[nama-tema]/
mkdir archetypes
touch default.md

Isi file default.md:

---
draft: true
date: {{ .Date }}
title: "{{ replace .TranslationBaseName "-" " " | title }}"
slug: {{ .BaseFileName }}

tags:
    - Python

categories:
    - Pemrograman


image: https://lorempixel.com/720/380
thumbnail: https://lorempixel.com/320/160
---

Kenapa kita membuat archetype?

Karena kita membutuhkan atribut image di dalam tema, jadi agar setiap pembuatan artikel baru front-matter-nya punya atribut image.

Silakan coba archetype-nya dengan membuat artikel baru:

hugo new post/artikel-baruku.md

Membuat Halaman Homepage

Setelah itu, mari kita buat halaman Homepage atau Landing page.

Kode halaman ini berada pada /layouts/index.html, isinya sebagai berikut:

<!DOCTYPE html>
<html>
    <head>
        {{ partial "head.html" . }}
    </head>
    <body>

        {{ partial "nav.html" . }}
        {{ partial "header.html" . }}

        {{ partial "post-grid.html" . }}

        {{ partial "footer.html" . }}
        {{ partial "js-init.html" . }}
    </body>
</html>

Kita hanya perlu meng-include partial yang sudah dibuat.

Caranya:

{{ partial "nama-partial.html" . }}

Pastikan menaruh tanda titik (.) di belakangnya agar kode template hugo di dalam partial dapat di-parse.

Gampang kan…

Untuk melihat hasilnya, silakan jalankan server hugo.

hugo server

atau jika temanya belum di-apply di konfigurasi:

hugo server -t [nama-tema]

Hasilnya:

Membuat Halaman Artikel

Halaman artikel menggunakan template dari file layouts/_default/single.html:

<!DOCTYPE html>
<html>
    <head>
        {{ partial "head.html" . }}
    </head>
    <body>

        {{ partial "nav.html" . }}
        {{ partial "header.html" . }}

        <div class="container mb-5">
            <div class="row">

                <div class="col-sm-12 col-lg-8 mb-5 card">
                    <article>
                        <div class="">
                           <div class="card-body p-md-5">
                                
                               <div class="post-header mb-5">
                                    
                                    <div class="mb-3">
                                        <img src="{{ .Site.Author.image }}" class="rounded-circle" width="32" height="32" />
                                        <a href="{{ .Site.Author.homepage }}">{{ .Site.Author.name }}</a> | <time>{{ .Date.Format "02-01-2006" }}</time>
                                    </div>

                                    <h2 class="card-title mb-3">{{ .Title }}</h2>
                                    
                                    <div>
                                        {{ range .Params.tags }}
                                        <span class="badge badge-success">
                                             #<a class="text-white" href="{{ "/tags/" | relLangURL }}{{ . | urlize }}">{{ . }}</a>
                                        </span> 
                                        {{ end }}
                                    </div>

                                </div>
                                <div class="post-content">
                                    {{ .Content }}
                                </div>
                                <div class="post-footer">
                                    {{ partial "disqus.html" . }}
                                </div>
                            </div>
                        </div>
                    </article>
                    </div>

                    <div class="col-sm-12 col-lg-4">
                       {{ partial "aside.html" . }}
                    </div>
            </div>
        </div>

        {{ partial "footer.html" . }}
        {{ partial "js-init.html" . }}
    </body>
</html>

Penjelasan:

  • Nilai .Site.Author akan diambil dari konfigurasi (config.toml) yang sudah dibuat;
  • Untuk menampilkan tags, kita gunakan perulangan {{ range .Params.tags }};
  • Nah untuk menampilkan konten artikelnya, kita gunakan variabel {{ .Content }}

Membuat Halaman List

Ada beberapa list yang akan ditampilkan:

  • List artikel di dalam tags dan category tertentu (taxonomy)
  • List artikel untuk page pagination

Semua list akan menggunakan template dari layouts/_default/list.html.

Karena yang akan kita tampilkan sama seperti halaman home, maka kita samakan saja kodenya:

<!DOCTYPE html>
<html>
    <head>
        {{ partial "head.html" . }}
    </head>
    <body>

        {{ partial "nav.html" . }}
        {{ partial "header.html" . }}

        {{ partial "post-grid.html" . }}

        {{ partial "footer.html" . }}
        {{ partial "js-init.html" . }}
    </body>
</html>

Apa Selanjutnya?

Akhirnya selesai juga…

Template ini setidaknya sudah bisa digunakan.

Selanjutnya tinggal melakukan penambahan, peningkatan, dan perbaikan bugs

Silakan juga coba-coba untuk membuat:

  • Layout Halaman 404.html;
  • Layout Halaman Kustom seperti Portfolio, Product, dll;
  • Layout Halaman Arsip;
  • dan lain-lain.

Jika ada yang ditanyakan silakan sampaikan melalui komentar.