Logo

dev-resources.site

for different kinds of informations.

Create a Hugo Theme from Scratch - II

Published at
10/15/2024
Categories
hugo
theme
tailwindcss
Author
tomowang
Categories
3 categories in total
hugo
open
theme
open
tailwindcss
open
Author
8 person written this
tomowang
open
Create a Hugo Theme from Scratch - II

This is a series of blog posts documenting my process of building a Hugo theme from scratch (https://github.com/tomowang/hugo-theme-tailwind). The series consists of four articles, and this is the second one:

Hugo Theme Basics

Before we start building the theme, we need to have a basic understanding of some terms, syntax, and directory structure of Hugo themes.

Basic Syntax

Hugo uses html/template to render content, and methods and variables are accessed through {{ }}. Commonly used syntax is as follows:

  • Variable rendering - {{ $address }}
  • Method call - {{ FUNCTION ARG1 ARG2 .. }}
  • Loop

    {{ range $array }}
        {{ . }} <!-- The . represents an element in $array -->
    {{ end }}
    
  • Conditional statements - with if else etc.

    {{ with .Params.title }}
        <h4>{{ . }}</h4>
    {{ end }}
    
    {{ if (isset .Params "description") }}
        {{ index .Params "description" }}
    {{ else }}
        {{ .Summary }}
    {{ end }}
    
  • Pipeline - {{ .Title | .RenderString }}

  • Context . and global context $.

  • Use hyphen - to remove extra empty strings

    <div>
      {{- .Title -}}
    </div>
    

For a detailed description of the syntax, you can refer to the official documentation https://gohugo.io/templates/introduction/.

Hugo also provides many built-in variables, which are rendered through the theme file to ultimately generate static pages:

  • Site - Site object
  • Pages - Aggregation of pages
  • Page - Single page object
  • Menu - Menu list
  • Menu entry - Single menu object
  • Taxonomy - Single taxonomy object

When using a theme to render page content, we need to call attributes or methods based on the corresponding variables to finally present the content we want.
For example, if we want to display the reading time of a single article, we can call the ReadingTime method (https://gohugo.io/methods/page/readingtime/) of the Page variable.
Some objects have many methods, you can refer to the official documentation for details https://gohugo.io/quick-reference/methods/.

Directory Structure

After we create a basic Hugo theme, we can use the command tree -L 2 -d . to view the basic structure of the theme directory:

.
β”œβ”€β”€ archetypes
β”œβ”€β”€ assets
β”‚Β Β  β”œβ”€β”€ css
β”‚Β Β  └── js
β”œβ”€β”€ data
β”œβ”€β”€ i18n
β”œβ”€β”€ layouts
β”‚Β Β  β”œβ”€β”€ _default
β”‚Β Β  └── partials
└── static
Enter fullscreen mode Exit fullscreen mode

The meanings of the relevant directories are as follows:

  • archetypes - Templates for creating new content pages
  • assets - Static resource files, usually processed through the resource pipeline
  • data - Used to store additional data, you can refer to https://gohugo.io/templates/data-templates/
  • i18n - Stores localization files
  • layouts - Core template layout files
  • static - Static files that will be copied directly to the final public directory

The most important one is the layouts directory, which determines the pages supported by the template and the layout of the pages.
Hugo uses a set of rules to query which template file should be used for the content page. A typical site consists of the following types of pages:

  • Home page
  • Single page
  • List page
  • Term page
  • Term list page

The lookup order can be referred to in the official documentation https://gohugo.io/templates/lookup-order/.
The official documentation describes the complex lookup order, but most page structures of the theme are similar, so in actual situations, only a few pages need to be defined to achieve the desired result.

Theme Content Construction

tailwindcss

Before starting to build, we need to install tailwindcss. I made some adjustments based on the official documentation:

# install
pnpm install -D tailwindcss
# init
npx tailwindcss init
Enter fullscreen mode Exit fullscreen mode

Install tailwindcss and generate tailwind.config.js configuration file through the above commands.
Since our page content is in the layout directory, we need to adjust the content configuration in tailwind.config.js:

content: ["./layouts/**/*.html"],
Enter fullscreen mode Exit fullscreen mode

Place the tailwindcss reference in assets/css/main.css:

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Then we can monitor the page content and generate the actual CSS file to be used through the following command:

npx tailwindcss -i assets/css/main.css -o ./static/main.css --watch
Enter fullscreen mode Exit fullscreen mode

Basic Layout

Based on my needs and the description of the Hugo directory structure in the previous section, I finally defined the following basic pages:

  • baseof.html - Carries the layout of the main page
  • list.html - List page, term list page
  • single.html - Single page, the main content page
  • terms.html - Term page

Through the baseof.html file, we define the basic template, which will be applied to all pages. Its core content is as follows:

<!DOCTYPE html>
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
<head>
  {{ block "title" . }}
    <title>
      {{ if .IsHome }}{{ $.Site.Title }}{{ with $.Site.Params.Subtitle }} β€”
      {{ . }}{{ end }}{{ else }}{{ .Title }} ::
      {{ $.Site.Title }}{{ with $.Site.Params.Subtitle }} β€” {{ . }}{{ end }}{{ end }}
    </title>
  {{ end }}
  {{ partial "head.html" . }}
</head>
<body class="w-full bg-slate-50">
  <header class="flex flex-none justify-center">
    {{ partial "header.html" . }}
  </header>
  <main class="flex flex-auto justify-center">
    {{ block "main" . }}{{ end }}
  </main>
  <footer class="flex flex-none justify-center">
    {{ partial "footer.html" . }}
  </footer>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

I use {{ partial "head.html" . }} to include the content required by the head tag, and refer to the CSS file generated earlier in head.html:

<!-- Theme CSS (/static/main.css) -->
<link rel="stylesheet" href="{{ "main.css" | relURL }}" />
Enter fullscreen mode Exit fullscreen mode

In the HTML body tag, the header tag is the top navigation, the footer tag is the bottom menu, and the main tag is the main content.
The content of {{ block "main" . }}{{ end }} in the main tag needs to be defined on other pages. For example, the simplified list.html code is as follows:

{{ define "main" }}
  <div class="flex flex-col w-full max-w-4xl lg:max-w-5xl relative">
    <div class="flex flex-row">
      <section class="flex flex-col w-2/3">
        {{ range (.Paginate $pages).Pages }}
          <article class="flex flex-col gap-y-3 p-6 mt-6 rounded-lg shadow-md bg-white">

            <h2 class="text-4xl font-semibold text-slate-800">
              <a href="{{ .RelPermalink }}">{{ .Title | markdownify }}</a>
            </h2>
          </article>
        {{ end }}
      </section>
      <aside class="flex flex-col w-1/3 mx-3 top-0 sticky self-start">
        {{ partial "sidebar.html" . }}
      </aside>
    </div>
    {{ partial "pagination.html" . }}
  </div>
{{ end }}
Enter fullscreen mode Exit fullscreen mode

Among them, the section tag corresponds to the article list block, and the aside tag corresponds to the side term display block.
The content of the single page single.html is similar, including the article title, TOC content, article body, etc.
The advantage of tailwindcss is that there is a ready-made typography plugin that can directly render the content of the article without having to define complex CSS styles additionally.

The overall reference relationship can refer to the following figure:

hugo theme layout structure

In order to reuse some components, such as tag lists, reading time, icons, etc., we can abstract the same content and put it in the layouts/partials directory.
Taking icons as an example, I use https://tabler.io/icons as the source of icons,
Download the corresponding icon SVG file to the assets directory and define a partial page:

{{- $iconFile := resources.GetMatch (printf "icons/%s.svg" .) -}}
{{- if $iconFile -}}
    {{- $iconFile.Content | safeHTML -}}
{{- else -}}
    {{- errorf "Error: icon '%s.svg' is not found under 'assets/icons' folder" . -}}
{{- end -}}
Enter fullscreen mode Exit fullscreen mode

Among them, the symbol . is the passed icon name parameter, so that we can quickly refer to the icon in the page, such as

{{ partial "icon" "calendar" }}
Enter fullscreen mode Exit fullscreen mode

Through about 5 days of spare time development (#bf54ab9),
the final effect is as follows:

hugo theme basic layout

Of course, this is the first phase of the content, only with a white theme (the theme switch button does not respond when clicked), no multilingual support, and no responsiveness.
But the main page structure is already there, and tailwindcss has shown its efficient and flexible characteristics in dealing with CSS styles.

hugo Article's
30 articles in total
Favicon
How to Deploy a Static Website with Hugo and GitHub Pages
Favicon
How to create a hugo theme with contact form
Favicon
πŸ›  Build a Professional CV in PDF with Markdown and Hugo
Favicon
Open source Hugo Theme for building SaaS websites: Saasify
Favicon
How to Host Hugo inΒ Vercel
Favicon
Create a Hugo Theme from Scratch - II
Favicon
Create a Hugo Theme from Scratch - III
Favicon
Create a Hugo Theme from Scratch - IV
Favicon
Life of a blog - blogging in 2024 and beyond
Favicon
Create a Hugo Theme from Scratch - I
Favicon
Using Hugo Render hooks to make links bend to your will
Favicon
Step-by-Step Guide to Deploy Hugo on Vercel
Favicon
Hugo shortcode for sns with unit.link like button
Favicon
Deploying Hugo from Self-Hosted GitLab to Cloudflare Pages
Favicon
Adding Search Functionality to a Hugo Static Site Based on INFINI Pizza for WebAssembly
Favicon
How to Build Personal Website for Free With Hugo
Favicon
My Obsidian + Hugo blogging setup
Favicon
How to schedule new blog posts
Favicon
Hello World
Favicon
Migrating my blog from Jekyll to Hugo
Favicon
[pt-BR] Hugo: Criando sua primeira aplicação
Favicon
My mistakes regarding the tech SEO optimization of my website
Favicon
Building static websites
Favicon
How to deploy a Hugo site to S3 in 2024
Favicon
Need advice on first website
Favicon
[pt-BR] Hugo: a forma mais rΓ‘pida de fazer um site
Favicon
Building a Dynamic Contact Form for Your Hugo Static Website
Favicon
Building a Blog With a CMS Using Hugo and Netlify
Favicon
Concept: Including dev.to Posts in a Static Hugo Site
Favicon
Build & Deploy a Site with Hugo & Firebase

Featured ones: