Hugo site from html theme(2/3)

For one of our customers we ported a commercial HTML theme to a Hugo site. Main challenge: Make every page widget-based and content manageable for the customer. We use PoppyGo as the CMS for the customer, but how can we enable the customer to add any section widget to any page?

A universal masterplan porting an HTML theme into HUGO templates

Let’s take a look at how we will split elements of the HTML files into HUGO templates.
See the image below.

HUGO already created following templates that are shown in the figure above.

In themes/myTheme

  • index.html. This is the homepage.

In themes/myTheme/layouts/_default/

  • baseof.html, taking care of the head, nav and footer and placeholder for main
  • list.html representing list pages
  • single.html representing detailpages

We will use these 4 templates to render all pages and sections in this guide. Every section and page in the site can be given a unique layout, by defining section partials for each page and each section. We will get to that later. First let’s dive into these 4 templates.

Baseof template

The baseof template takes care of all the returning elements in every page, like header, navbar and footer. In block “main” the actual content of a page resides.

Our baseof template looks like this. Copy this code in baseof.html in folder themes/myTheme/layouts/_default/. This will do for any HTML template that you want to port.

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

Head partial

Create a file called “head.html” into the themes/myTheme/layouts/partials folder. Then copy the head of the HTML theme into the head partial.

In the Ketan HTML theme, the head looks like this:

    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="assets/css/bootstrap.min.css">
    <!-- Animate CSS -->
    <link rel="stylesheet" href="assets/css/animate.min.css">
    <!-- Meanmenu CSS -->
    <link rel="stylesheet" href="assets/css/meanmenu.css">
    <!-- Boxicons CSS -->
    <link rel="stylesheet" href="assets/css/boxicons.min.css">
    <!-- Owl Carousel CSS -->
    <link rel="stylesheet" href="assets/css/owl.carousel.min.css">
    <!-- Owl Carousel Default CSS -->
    <link rel="stylesheet" href="assets/css/owl.theme.default.min.css">
    <!-- Odometer CSS -->
    <link rel="stylesheet" href="assets/css/odometer.min.css">
    <!-- Magnific Popup CSS -->
    <link rel="stylesheet" href="assets/css/magnific-popup.min.css">
    <!-- Imagelightbox CSS -->
    <link rel="stylesheet" href="assets/css/imagelightbox.min.css">
    <!-- Style CSS -->
    <link rel="stylesheet" href="assets/css/style.css">
    <!-- Responsive CSS -->
<link rel="stylesheet" href="assets/css/responsive.css">

<title>Ketan - Kindergarten & School HTML Template</title>

    <link rel="icon" type="image/png" href="assets/img/favicon.png">

You can see that there are quite a lot of CSS dependencies that take a request to get them. For performance, it’s better to bundle these files. Now, this is where Hugo comes in handy.

Create a file called bundleCSS.html into /themes/myTheme/layouts/partials/process. In this file we write a short Hugo code to bundle these assets at build time. Copy this code into your bundleCSS.html and replace the css dependencies with those of your theme.

{{ $bootstrap := resources.Get "css/bootstrap.min.css" }}
{{ $meanmenu := resources.Get "css/meanmenu.css" }}
{{ $animate := resources.Get "css/animate.min.css" }}
{{ $boxicons := resources.Get "css/boxicons.min.css" }}
{{ $owl_carousel := resources.Get "css/owl.carousel.min.css" }}
{{ $owl_theme := resources.Get "css/owl.theme.default.min.css" }}
{{ $odometer := resources.Get "css/odometer.min.css" }}
{{ $magnific := resources.Get "css/magnific-popup.min.css" }}
{{ $lightbox := resources.Get "css/imagelightbox.min.css" }}
{{ $style := resources.Get "css/style.css" }}
{{ $responsive := resources.Get "css/responsive.css" }}
{{ $css := slice  $bootstrap $meanmenu $animate $boxicons $owl_carousel $owl_theme $odometer $magnific $lightbox $style $responsive  | resources.Concat "css/ketan.css" }}

{{- $css = $css | minify | fingerprint "md5" -}}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">

This partial now takes all css dependencies, concatenates, minifies and fingerprints them. So your CSS now is optimised for performance. You could use postCSS to get rid of all unused css, but we skip that part in this guide.

Now, open your head.html and replace the code of the HTML template with the bundleCSS partial.

The head.html file now looks like this:

  <link rel="icon" href="{{ "img/favicon.png" | absURL }}" type="image/png" />
  {{- partial "process/bundleCSS" . -}}
  <title>{{ .Title }}</title>

Create a file called navbar.html in folder themes/myTheme/layouts/partials/ Now copy the Nav area in yout html theme into this file.

Below you see a piece of our navbar.html partial. It offers a menu with dropdown possibilities. We have replaced the actual content of the navar with a range .site.Menu.main that looks if an item has children. If so, it creates a dropdown.

<!-- Start Navbar Area -->
<div class="navbar-area">
    <div class="main-responsive-nav">
        <div class="container">
            <div class="main-responsive-menu">
                <div class="logo">
                    <a href="index.html">
                        <img src="img/logo.png" alt="image">
    <div class="main-navbar">
        <div class="container">
            <nav class="navbar navbar-expand-md navbar-light">
                <a class="navbar-brand" href="index.html">
                    <img src="/img/logo.png" alt="image">

                <div class="collapse navbar-collapse mean-menu" id="navbarSupportedContent">
                    <ul class="navbar-nav">
                      {{ $currentPage := . }}
                      {{ range .Site.Menus.main }}
                          {{ if .HasChildren }}
                              <li class="{{ if $currentPage.HasMenuCurrent "main" . }}active{{ end }} nav-item">
                                  <a href="#" class="nav-link">
                                      {{ .Pre }}
                                      <span>{{ .Name }}</span>
                                      <i class='bx bx-chevron-down'></i>
                                  <ul class="dropdown-menu">
                                      {{ range .Children }}
                                          <li class="{{ if $currentPage.IsMenuCurrent "main" . }}active{{ end }}">
                                              <a href="{{ .URL }}">{{ .Name }}</a>
                                      {{ end }}

                          {{ else }}
                              <li class="nav-item">
                                  <a href="{{ .URL }}" class="nav-link">
                                      {{ .Pre }}
                                      <span>{{ .Name }}</span>
                          {{ end }}
                      {{ end }}
<!-- End Navbar Area -->

Now, where do the values come from to populate the menu? These are in the config file.

In your config file you create your menu. Below is our menu. Notice that the parent parameter defines if a menu item is in a dropdown below a main item.

    - identifier: ''
      name: Home
      parent: ''
      url: /
      weight: 1
    - identifier: ''
      name: Over ons
      parent: ''
      url: '/#who-we-are'
      weight: 2
    - identifier: cursussen
      name: Tekenlessen
      parent: ''
      url: /cursussen/
      weight: 3
    - identifier: pakket1
      name: Pakket1
      parent: cursussen
      url: /cursussen/tekenen1
      weight: 1
    - identifier: pakket2
      name: Pakket2
      parent: cursussen
      url: /cursussen/tekenen2
      weight: 0

So, the navbar is now working and based on content manageable content.

Go to part 3