Converting Simple Web Page Into Hugo Theme Part 2 - Theme Home Page

February 9, 2019

Creating Hugo theme

Before we start with theme, lets first create our blog and add few posts.

Run hugo new site blogv1 in some folder where you’d like to to put your blog.

Then cd into blogv1 and create two posts:

hugo new posts/my-first-post.md

hugo new posts/my-second-post.md

and paste some content (from good old) into the newly created files.

Then run hugo server -D and open localhost:1313 in your browser. We’ll get an empty page because we didn’t specify any theme. Leave blog like that till we create our own theme.

Hugo themes are basically plain HTML fragments, populated with content from markdown files and composed in certain way. Themes themselves don’t add or require any CSS or HTML and Hugo doesn’t care what we are templating.

Folder structure

cd into blogv1 and create our theme (called only-bones) by running hugo new theme only-bones.

Created structure looks like this:

blogv1/
├── archetypes
│   └── default.md
├── layouts
│   ├── 404.html
│   ├── _default
│   │   ├── baseof.html
│   │   ├── list.html
│   │   └── single.html
│   ├── index.html
│   └── partials
│       ├── footer.html
│       ├── header.html
│       └── head.html
├── LICENSE
├── static
│   ├── css
│   └── js
└── theme.toml

Most interesting folder is layouts, it will contain fragments for our theme. baseof.html will contain structure that will be inherited for all our pages. list.html will handle showing list of posts. single.html will handle showing single post.

We are most interested into list.html and single.html pages. These pages will include some fragments from partials folder.

Splitting HTML into fragments

Currently, our HTML for home page with list of articles looks like this:

<!DOCTYPE html>
<html>
<head>
	<title>Esed Alihodzic</title>
  <meta name="viewport" content="width=device-width">
  <link href="style.css" rel="stylesheet">
</head>

<body>
	<div class="wrapper">

        <nav class="main-nav">

            <h3>Esed Alihodzic</h3>
            <ul class="nav">
                <li><a href="index.html">Blog</a></li>
                <li><a href="#">About</a></li>
            </ul>

            <ul class="social">
                <li><a href="#">G</a></li>
                <li><a href="#">T</a></li>
                <li><a href="#">Li</a></li>
                <li><a href="#">M</a></li>
            </ul>
        </nav>

        <div class="content">
            <article>
              <h1>Article With Image</h1>
              <p class="article-detail-date">February 8, 2018</p>
              <p>In this layout, we display the areas in source order for any screen less...</p>

              <a href="article.html">Read more...</a>
            </article>

            <div class="page-nav">
              <a href="#">< Newer</a>
              <a href="#">Older ></a>
            </div>
      </div>
  </div>
</body>
</html>

Copy and paste our style.css into blogv1/themes/only-bones/static/css.

Lets chop out HTML into some fragments.

Header fragment

Lets first place header into its fragment. Our header looks like this:

<head>
	<title>Esed Alihodzic</title>
  <meta name="viewport" content="width=device-width">
  <link href="style.css" rel="stylesheet">
</head>

Now open partials/header.html fragment and replace its content with our header with one change:

<head>
	<title>{{ .Site.Title }}</title>
	 <meta name="viewport" content="width=device-width initial-scale=1">
	 <link href="{{ "css/style.css" | relURL }}" rel="stylesheet">
</head>

Fragment is almost the same with exception of title tag which is changed to let Hugo know to populate it from theme configuration so that title can be changed without changing fragments.

Lets do the same for navigation with few additional changes. Current nav looks like this:

<nav class="main-nav">

    <h3>Esed Alihodzic</h3>
    <ul class="nav">
        <li><a href="index.html">Blog</a></li>
        <li><a href="#">About</a></li>
    </ul>

    <ul class="social">
        <li><a href="#">G</a></li>
        <li><a href="#">T</a></li>
        <li><a href="#">Li</a></li>
        <li><a href="#">M</a></li>
    </ul>
</nav>

Paste it into partials/sidebar.html file and change to look like this:

<nav class="main-nav">
  <h3>{{ .Site.Title }}</h3>

   <ul class="nav">
        <li><a href="index.html">Blog</a></li>
        <li><a href="#">About</a></li>
    </ul>

    <ul class="social">
        <li><a href="#">G</a></li>
        <li><a href="#">T</a></li>
        <li><a href="#">Li</a></li>
        <li><a href="#">M</a></li>
    </ul>
</nav>

Main Fragment

Lets also create main fragment from main html that looks like this:

<article>
  <h1>Article With Image</h1>
  <p class="article-detail-date">February 8, 2018</p>
  <p>In this layout, we display the areas in source order for any screen less that 500 pixels...</p>

  <a href="article.html">Read more...</a>
</article>

<div class="page-nav">
  <a href="#">< Newer</a>
  <a href="#">Older ></a>
</div>

This part shows list of posts so it goes into _default/list.html:

{{ define "main" }}

<div class="content">

	{{ $paginator := .Paginate (where .Pages "Type" "posts") }}

	{{ range $paginator.Pages }}

	<article>

	<h1>{{.Title}}</h1>
	<p class="article-detail-date">{{ .Date.Format "January 2, 2006" }}</p>
	<p>{{ .Summary | plainify | safeHTML }}</p>

	{{ if .Truncated }}
	<a href="{{ .Permalink }}">Read more</a> 
	{{ end }}

    </article>

	{{ end }}

	<div class="page-nav">

      {{ if .Paginator.HasPrev }}
      <a href="{{ .Paginator.Prev.URL }}">
        {{- partial "svg/icons" "chevron-left" -}} Newer
      </a>
      {{ else }}
      <div></div>
      {{ end }}

      {{ if .Paginator.HasNext }}
      <a href="{{ .Paginator.Next.URL }}">
        Older {{- partial "svg/icons" "chevron-right" -}}
      </a>
      {{ end }}

    </div>

</div>

{{ end }}

There is a bit more going on here. Lets see what. List of posts in paginated and list.html page uses built-in paginator to navigate through pages.

{{ $paginator := .Paginate (where .Pages "Type" "posts") }}
{{ range $paginator.Pages }}

After that we have part of code that handles displaying actual article attributes:

<article>
<h1>{{.Title}}</h1>
<p class="article-detail-date">{{ .Date.Format "January 2, 2006" }}</p>
<p>{{ .Summary | plainify | safeHTML }}</p>

{{ if .Truncated }}
<a href="{{ .Permalink }}">Read more</a> 
{{ end }}
</article>

That is pretty self-explanatory.

After that we have part of code that handles showing pagination buttons/links:

<div class="page-nav">
	{{ if .Paginator.HasPrev }}
	<a class="pagination" href="{{ .Paginator.Prev.URL }}">
	Newer
	</a>
	{{ else }}
	<div></div>
	{{ end }}

	{{ if .Paginator.HasNext }}
	<a class="pagination" href="{{ .Paginator.Next.URL }}">
	Older
	</a>
	{{ end }}
</div>

With all that explained, lets move code that handles showing posts info into a separate fragment and also do the same with pagination.

Just copy/paste it into _default/summary.html.

Copy/paste pagination code into partials/pagination.html.

After doing this changes, our list.html now looks like this:

{{ define "main" }}
<div class="content">

	{{ $paginator := .Paginate (where .Pages "Type" "posts") }}

	{{ range $paginator.Pages }}

	{{ .Render "summary"}}

	{{ end }}

	{{ partial "pagination" . }}

</div> 
{{ end }}

Now we have completed page structure for showing list of posts and our list.html page now looks like this:

{{ define "main" }}

<div class="content">

	{{ $paginator := .Paginate (where .Pages "Type" "posts") }}

	{{ range $paginator.Pages }}

	{{ .Render "summary"}}

	{{ end }}

	{{ partial "pagination" . }}
</div> 
{{ end }}

_default/baseof.html now looks like this:

<!DOCTYPE html>
<html>

{{- partial "header" . -}}

<body>
	<div class="wrapper">

		{{- partial "sidebar" . -}}

		{{- block "main" . }}{{- end }}

	</div>
</body>
</html>

Only main section will change depending on shown content.

Now in blogv1 run hugo server -D --disableFastRender and open localhost:1313 in browser, you should see an empty page.

Now open localhost:1313/posts and you should see sidebar and list of summaries of two posts we created.

To show list of posts on home page we have to edit layouts/index.html page and add {{ .Render "_default/list"}}. This line tells Hugo to render list of posts instead of home page. Opening localhost:1313 should now show list of posts.

In the next part we’ll see how to create layout for single post and about page that will also inherit _default/baseof.html.

References