Hugo Theme (Part 1) : Setup

Intro

When I started this blog, I used the Hugo theme Anatole. I liked the minimal design but still had to modify a few things. I recently started working on a new theme using Tailwind CSS. This blog series will go over some implementation details. If you looking for a guide to use the theme check out the other post Hugo Jack Theme

Check out the source at https://github.com/yasakdogra/jack

[!NOTE]

This theme is still in development. I am constantly adding more features and making changes. It’s also not very optimized.

Prerequisites

Before creating a theme, we can need to get some things ready. Let’s check what we need first. We need to use Tailwind CSS. During development, we can use the Tailwind CLI to watch for changes in our code. In production, if we want to build and deploy our site automatically using providers like Cloudflare Pages, it will be a bit hard to use Tailwind CLI. Thankfully, Hugo has built in support for PostCSS and we can use Tailwind as a PostCSS plugin.

We can add Tailwind CSS and PostCSS as NPM dependencies. I like to keep the dependencies at site level rather than theme level. So in our site directory, we can add a package.json file with the dependencies. The following commands will also do the same.

Initialize NPM

npm init

Add PostCSS

npm add -D postcss postcss-cli autoprefixer

Add Tailwind CSS

npm add -D tailwindcss @tailwindcss/typography

We also need a package that can run multiple commands, so we can let both Hugo CLI and Tailwind CLI watch for file changes. We will add the npm-run-all package

npm add -D npm-run-all

Add the NPM commands for building and running development server

package.json

{
	"scripts": {
    "dev:jack": "tailwindcss -c ./themes/jack/tailwind.config.js -i ./themes/jack/assets/css/main.css -o ./themes/jack/assets/_gen/css/main.css --watch",
    "dev:hugo": "hugo server --noHTTPCache",
    "dev:all": "npm-run-all -p dev:jack dev:hugo",
    "dev": "npm-run-all -p dev:jack dev:hugo",
    "build": "hugo --cleanDestinationDir --minify"
  },
}

Setup

Initialize a basic Hugo theme with name jack

hugo new theme jack

Change to theme folder and add Tailwind config

npx tailwindcss init

In tailwind.config.js, add location the files where we are going to use Tailwind utility classes. We can add all the files in the layout folder of our theme. We will also enable the Tailwind Typography plugin and dark mode with CSS class

module.exports = {
		darkMode: 'class',
    content: ['./themes/jack/layouts/**/*.html'],
    theme: {},
    plugins: [
        require('@tailwindcss/typography'),
    ],
}

We can add more things to this file later as we need them.

In postcss.config.js, add tailwind as plugin and specify the location of its config file

module.exports = {
    plugins: {
      tailwindcss: {
        config: './themes/jack/tailwind.config.js',
      },
      autoprefixer: {},
    }
}

In assets/css/main.css, delete the existing CSS and add Tailwind directives

@tailwind base;
@tailwind components;
@tailwind utilities;

In layouts/_default/baseof.html, let’s add our html skeleton

<!DOCTYPE html>
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}"
    dir="{{ or site.Language.LanguageDirection `ltr` }}">

<head>
    {{ partial "head.html" . }}
</head>

<body>
		{{ partial "header.html" . }}

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

    {{ partial "footer.html" . }}

    {{ partial "scripts.html" . }}
</body>

</html>

In layouts/partials/head.html, we will add link to our CSS file. For development environment, we will use Tailwind CLI and css/main.css to generate the CSS file _gen/css/main.css. We can later add the _gen folder to gitignore so it does’t get checked into version control. For production mode, we will use the css/main.css file and pass it to PostCSS which will use Tailwind plugin.

{{- if eq hugo.Environment "development" }}
  {{- with resources.Get "_gen/css/main.css" }}
    <link rel="stylesheet" href="{{ .RelPermalink }}">
  {{- end }}
{{- else }}
  {{- with resources.Get "css/main.css" | postCSS | minify | fingerprint }}
    <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
  {{- end }}
{{- end }}

In .gitignore, add entry to ignore the assets/_gen folder

assets/_gen/

We will add a javascript file too.

In layouts/partials/head/js.html

{{- with resources.Get "js/main.js" }}
  {{- if eq hugo.Environment "development" }}
    {{- with . | js.Build }}
      <script src="{{ .RelPermalink }}"></script>
    {{- end }}
  {{- else }}
    {{- $opts := dict "minify" true }}
    {{- with . | js.Build $opts | fingerprint }}
      <script src="{{ .RelPermalink }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
    {{- end }}
  {{- end }}
{{- end }}

We are now ready to add some styles. For the html we can edit, we will use the utility classes in html. For the HTML generated by Hugo, we can add CSS in our assets/main.css file and use Tailwind’s @apply directive.

Let’s make our html and body blocks full screen. In layouts/_default/baseof.html

<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}"
    dir="{{ or site.Language.LanguageDirection `ltr` }}"
    class="min-h-screen"
<body class="min-h-screen px-4">

Use flex box for body and auto margins. Give it default text and background colors. Use dark: to use classes in dark mode.

<body class="min-h-screen px-4
             container flex flex-col mx-auto 
             bg-stone-100 dark:bg-slate-700 text-slate-800 dark:text-neutral-50">

Wrap the main block with a flex box and add typography to it

<div class="flex flex-col flex-grow mt-8 max-w-none
prose prose-xl prose-amber dark:prose-invert"
>
{{ block "main" . }}{{ end }}
</div>

Define the default font weight, link colors and some other things in tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
    theme: {
        extend: {
            typography: ({ theme }) => ({
                amber: {
                    css: {
                        'font-weight': '400',
                        '--tw-prose-links': theme('colors.amber[600]'),
                        '--tw-prose-invert-links': theme('colors.amber[500]'),
                        'blockquote p:first-of-type::before': { content: 'none' },
                        'blockquote p:first-of-type::after': { content: 'none' },
                    },
                },
            }),
        },
    },
}

We can also modify Tailwind prose styles in our HTML by using prose-<element>:.

Remove the bold and underline from links and change heading size

<div class="flex flex-col flex-grow mt-8 max-w-none
prose prose-xl prose-amber dark:prose-invert 
prose-a:font-normal prose-a:no-underline prose-h1:text-4xl"
>

Remove the tick marks from inline code blocks

<div class="flex flex-col flex-grow mt-8 max-w-none
prose prose-xl prose-amber dark:prose-invert 
prose-a:font-normal prose-a:no-underline prose-h1:text-4xl
prose-code:before:content-none prose-code:after:content-none prose-code:font-normal"
>

We will modify more prose styles later as required.

Thank you for reading. Check out the other parts in the series below.