Static site generators like Hugo or 11ty are a great alternative to database-driven content management systems (CMS),such as WordPress, Drupal, and Joomla. They are growing in popularity because they are great tools to build fast static websites based on raw data and a set of templates.
If you want to learn more about 11ty, head on over to Eleventy Documentation, 11ty Rocks!, and Kevin Powell, Turn static HTML/CSS into a blog with CMS using the JAMStack.
Hugo is one of the most popular open-source static site generators written in Go. It is fast, ease of use, and flexible. Hugo supports markdown JustToThePoint, Markdown and LaTeX.
Installing Hugo. If you are in Arch (GNU/Linux): sudo pacman -Syu go hugo. I prefer a software package manager because it makes it easier to install, update, or uninstall Hugo. Another alternative: brew install hugo.
To verify your install type: hugo version.
Create a new site: hugo new site justtothepoint
Create a new (skeleton) theme: hugo new theme anawim. Then, copy the layout file folders into your Hugo files directory,
Add some content. Create a new home page: hugo new _index.md. Create a new post: hugo new posts/my-new-post.md
A layout is a page used as a “frame”, “skeleton” or “basic template” to display your content. It consists of all the parts that remain consistent across all of the pages. Hugo uses a layout for the home page, a second one for content pages, and a third one that shows or displays lists of pages.
The main home page layout will live in themes/anawim/layouts/index.html, where anawim is my theme. This file defines the HTML structure or skeleton for the site’s home page:
{{ define "main" }}
<div class="container mx-auto">
<h1> {{ .Site.Title }} </h1>
{{ .Site.Title }} specifies that we want to pull the title field out of the config.yaml, the main Hugo’s configuration file.
{{if .Params.subtitle }}
<h2 class="text-secondary h3"> {{ .Params.subtitle }} </h2>
{{ end }}
<p> {{ .Description }} </p>
{{ .Content | markdownify }}
</div>
The {{ .Content | markdownify }} line displays the content of the page. This content will be in a Markdown document in our content folder, more specifically, in the “scope” or context" that contains the data we want to access. The current context is represented by a dot “.” and the content’s file is a file named _index.md in the content directory. It takes this content and runs it through Hugo’s Markdown processor.
The layouts directory contains all the HTML files that are used for generating HTML from the Markdown files. In other words, they specify how your content will be rendered. The _default directory is the place where Hugo will search for the base page layout file, that is, baseof.html:
<!doctype html>
<!-- prefix="og: http://ogp.me/ns#" is the required prefix for open graph protocol meta tags. -->
<html prefix="og: http://ogp.me/ns#" lang="{{ .Site.Language.Lang }}" >
<!-- Schema.org provides a collection of shared vocabularies webmasters can use to mark up their pages
in ways that can be understood by the major search engines! -->
<head itemscope itemtype="https://schema.org/WebPage">
{{- partial "mymeta" . -}} <!-- SEO: meta tags -->
{{- partial "head" . -}} <!-- Google Analytics -->
</head>
<body>
{{- partial "search-form" . }} <!-- Search with Lunr -->
{{- partial "navbar" . -}} <!-- Menu, Navbar -->
{{- block "main" . -}} {{ end }}
{{- partial "script-footer" . -}} <!-- Javascript -->
{{- partial "footer" . -}} <!-- Social Media, Copyright, Privacy Policy and Terms of Use -->
{{- partial "style" . -}} <!-- Css -->
{{- partial "search-index.html" . }} <!-- Search with Lunr -->
</body>
</html>
Observe that we define blocks and partial where we define the different “sections” or “pieces” of our content, e.g., the first two partials (mymeta, head) contain the code that will appear in the HEAD. The dash “-” removes whitespace characters in the output, so we are using it in every partial to reduce the number of blank lines that Hugo generates.
Under the layouts folder, you can see there is a folder called partials. It contains all our partial templates.
list.html is the “frame” or “html template” that Hugo uses to list posts of your site under a particular directory. single.html is the templated used to display a single page.
The layouts folder is where you define your layouts and templates, i.e., your site’s look and feel. They are used to render your content files written in Markdown (.md) in HTML webpages (.html).
The static directory contains your static assets, that is, your html pages, images, fonts, and any other assets that are not generated by Hugo. Everything inside of Hugo’s static/ directory goes to the root of the built site (public/) by default, so there’s no static directory in the build/deployable site.
The static directory will typically contains a fonts directory with your fonts, e.g., /static/fonts/NotoColorEmoji-Regular.ttf. However, you should have an assets directory with your CSS files because assets can be used in pipes, files inside your static folder can’t.
In my assets, there are a css folder with Bootstrap’s production-ready CSS and my main.css file.
# Emoji Support
@font-face {
font-family: 'Noto Color Emoji'; #
src: url('/fonts/NotoColorEmoji-Regular.ttf') format('truetype');
}
[...]
p {
font-family: Arial, "Noto Color Emoji", sans-serif;
font-size: 20px;
padding: 10px;
color: black;
}
.emoji {
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols", sans-serif;
}
CSS files are typically fingerprinted, bundle, and minimized.
The themes directory holds our theme “anawim”. It could contain a third-party theme to style your website.
We will also create a folder called data to access data formatted in JSON. It acts like a read-only database for your site.
Besides, there are an archetypes folder where you can define parameters and content for new pieces of content; a config.yaml, Hugo’s main configuration file where you can set your site’s configuration options; and contents. content contains all the markdown files with the content for your site.
Syntax Highligher. Generate a syntax highlighter CSS: hugo gen chromastyles –style=monokai > syntax.css.
Next, we will include this CSS in our /static/css/ directory and link it (head.html).
<link rel="stylesheet" href="{{ "css/syntax.css" | absURL }}" />
Awesome Fonts. Firstly, you need to download the FontAwesome zip and extract the folder. Second, copy the all.min.css file in our /static/css/ directory and link it.
<link rel="stylesheet" href= "{{ "css/all.min.css" | absURL }}" />
Finally, copy the webfonts folder from the downloaded zip file and place it into the project one directory above the all.min.css file.
Usage:
<i class="fab fa-github"></i><i class="far fa-smile-wink"></i><i class="fa-brands fa-linux"></i><i class="fa-solid fa-puzzle-piece"></i>
Escaping in Hugo
If you want to write about shortcodes, you need to replace the opening greater-than sign “<” with < or “%” with %.
By default, Hugo replaces two hyphens for a single dash, “–” the solution is this: ‐‐ ‐‐
The same goes with emojis, instead of :star: ⭐️, you use an HTML entity for one of the colons in the emoji code: :star:
If you want that Hugo does not turn ‐ into “‐”, you need to replace the ampersand “&” with & e.g., content_new = pattern.sub('&hyphen;&hyphen;', content_new)
content_new = pattern.sub("‐‐", content_new)
I also use this shortcode -spam.html-, htmlEscape returns the given string with the reserved HTML coded escaped:
<span {{ with .Get "style"}} style="{{ . | safeCSS }}"{{ end }}>{{ .Get "text" | htmlEscape }}</span>
First, we use the .Get function to retrieve the style parameter passed to our shortcode (e.g., “color:red;”). The dot’s value is different from the value inside the “with” block, it changes based on context, which is our style in this case. safeCSS declared the provided string as a “safe” CSS string. Next, we retrieve the text parameter and pipe it into htmlEscape.
Use:
{{< span style="color:red;" text="‐" >}}
Result: ‐
Footnotes The only consideration if that we need to enable footnotes in Hugo’s main configuration file (config.yaml, check the smiley 😄).1
That's a text with a footnote.[^1]
[^1]: This is the footnote's text.
It is also cool to style it in our main.css:
/* Styling the footnotes*/
a.footnote-ref {
background-color: #fff9c7;
}
a.footnote-ref::before {
content: '['; /* You can replace it with { */
}
a.footnote-ref::after {
content: ']'; /* You can replace it with } */
}
Comments with Cactus Comments.
Cactus Comments is a federated comment system built on Matrix. It respects your privacy, and puts you in control. The entire thing is completely free and open source.
First, you must have a Matrix account and register your site with Cactus Comments. Using Element, start a new chat with @cactusbot:cactus.chat and type:
register YOUR-SITE-NAME # e.g., register JustToThePoint
You should receive a confirmation: Created site YOUR-SITE-NAME for you 🚀
Add a Shortcode: layouts/shortcode/chat.html
<script type="text/javascript" src="https://latest.cactus.chat/cactus.js"></script>
<link rel="stylesheet" href="https://latest.cactus.chat/style.css" type="text/css">
<div id="comment-section"></div>
<script>
initComments({
node: document.getElementById("comment-section"),
defaultHomeserverUrl: "https://matrix.cactus.chat:8448",
serverName: "cactus.chat",
siteName: "<YOUR-SITE-NAME>",
commentSectionId: "{{ index .Params 0 }}"
})
</script>
The last step is to add the following code at the end of your posts:
{{< chat cactus-comments >}}
Another option is to create a partial (/layout/partials/chat.html) with the following code:
<script type="text/javascript" src="https://latest.cactus.chat/cactus.js"></script>
<link rel="stylesheet" href="https://latest.cactus.chat/style.css" type="text/css">
<div id="comment-section"></div>
<script>
initComments({
node: document.getElementById("comment-section"),
defaultHomeserverUrl: "https://matrix.cactus.chat:8448",
serverName: "cactus.chat",
siteName: "JustToThePoint.com",
commentSectionId: "cactus-comments"
})
</script>
And call this partial from baseof.html:
[...]
{{- block "main" . -}} {{ end }}
[...]
{{- partial "chat" . }}
Let’s open config.yaml:
baseURL: "https://justtothepoint.com/" # url address of the website | required
theme: "anawim"
pygmentsUseClasses: true # We are going to use a style sheet for highlighting.
pygmentsCodefences: true # It enable syntax highlighting in code fences with a language tag (html, python, css, json, md, etc.) in markdown: ``` html [ my code html ] ```
PygmentsStyle: "monokai"
languageCode: en-us
title: JustToThePoint # The title of the site.
copyright: "Copyright © 2022 JustToThePoint. All rights reserved." # Copyright notice for your site, typically displayed in the footer.
googleAnalytics: YourCodeHere # It enables Google Analytics by providing your tracking ID.
defaultContentLanguage: en # Content without language indicator will default to this language.
enableEmoji: true # It enables Emoji emoticons support.
enableRobotsTXT: true # It enables generation of robots.txt file. It tells search engine crawlers which URLs the crawler can access on your site.
assetDir: "assets/"
Without adding a single line to our config file, Hugo will automatically create taxonomies for tags and categories. I don’t want Hugo to create any taxonomies, so I set disableKinds in config.yaml as follows:
disableKinds:
- taxonomy
- term
markup: # Configure Markup
goldmark: # Goldmark is the default library used for Markdown.
extensions:
footnote: true # It enables footnote. 😄
renderer:
unsafe: true # By default, Goldmark does not render raw HTML.
highlight: # This is the highlight configuration. Hugo uses Chroma as its code highlighter.
codeFences: true # It enables syntax highlighting with GitHub flavored code fences: ``` html [ my code html ] ``` By default, Highlighting is carried out via the built-in shortcode highlight.
guessSyntax: true # It tries to do syntax highlighting on code fenced blocks in markdown without a language tag.
lineNos: false # It configures line numbers.
tabWidth: 4
params:
author: Máximo Núñez Alarcón, Anawim
og_image: https://www.justtothepoint.com/myImages/logotipoMagotipoCabecera.png
description: Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.
params:
social:
profiles:
facebook: "https://www.facebook.com/nmaximo7"
github: "https://github.com/nmaximo7"
twitter: "http://Twitter.com/justtothepoint"
instagram: "http://instagram.com/justtothepoint"
linkedin: "https://www.linkedin.com/in/justtothepoint"
youtube: "https://youtube.com/justtothepoint"
NPM is a package manager for JavaScript. It consists of a command line client (npm) and an online database of public and paid-for private packages, called the npm registry.
Node.js (Node) is a run time open source development platform. It allows us to write JavaScript code that runs directly in a computer process instead of in a browser, so it is used to write server-side applications with access to the operating system, file system, and everything else required to build fully-functional applications.
Initialize the project: npm init (project’s name, initial version, description, etc.) It will generate a package.json file in the current directory. A package.json file is your project’s manifest. It includes the packages and applications it depends on, information about its unique source control, and specific metadata.
You can install modules with npm install myModule or install modules and save them to your package.json as a dependency: npm install –save-dev npm-check. We are going to install npm-check and hugo-installer.
npm-check checks for outdated, incorrect, and unused dependencies. hugo-installer installs Hugo into your repository: npm install hugo-installer –save-dev
Edit the package.json file. JSON does not support comments.
{
{
"name": "justtothepoint", // The name of the project.
"version": "1.0.0", // The version of the project.
"description": "Free resources, bilingual e-books and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.", // The description of the project
"main": "index.js",
"scripts": {
"dev": "exec-bin node_modules/.bin/hugo/hugo server --gc --disableFastRender",
// It runs Hugo's webserver: -gc remove unused cache files after the build; --disableFastRender enables full re-renders on changes.
"build": "exec-bin node_modules/.bin/hugo/hugo --gc -cleanDestinationDir --minify",
// --cleanDestinationDir removes files from destination not found in static directories. --minify reduces CSS code size and make your website load faster.
"prebuild": "exec-bin node_modules/.bin/hugo/hugo --gc --cleanDestinationDir",
"npm-check": "npm-check -u",
"start": "exec-bin node_modules/.bin/hugo/hugo server -d-disableFastRender --gc --renderToDisk --cleanDestinationDir",
// --renderToDisk serve all files from disk. Please notice that the default is to serve all files from memory.
"postinstall": "hugo-installer --version otherDependencies.hugo --extended --destination node_modules/.bin/hugo"
// hugo-installer is recommended as part of our postinstall hook within our project's package.json. It installs Hugo into the repository. --destination is the path to the folder into which the binary will be put. --extended download the extended version of Hugo.
},
"author": "Máximo Núñez Alarcón, Anawim", // The author of the project.
"license": "ISC", // The license of the project.
"devDependencies": {
"npm-check": "^5.9.2"
},
"dependencies": {
"exec-bin": "^1.0.0",
"hugo-installer": "^3.1.0"
},
"otherDependencies": {
"hugo": "0.96.0"
}
}
Credits: geekthis, Hugo Footnotes and Citations ↩︎