Création d'un site avec Hugo

Vous trouverez dans cet article, l’ensemble des éléments permettant de créer un site web static en se basant sur le générateur Hugo.

Les sujets abordés sont la structuration du site, la création d’un thème, la gestion d’un flux rss, la mise en place du multi-langue, …

Qu’est ce que Hugo

Hugo est un générateur de site web static écrit en Go et créé par Steve Francia en 2013 (wikipedia).

La documentation est très bien faite pour prendre en main rapidement les bases de l’outil.

Objectif

  • Avoir un système de blog lié avec des tags permettant de naviguer sur les sujets et avoir un esprit documentation sans être un wiki
  • Ne pas avoir de dépendance inutile avec des ressources extérieurs (google,facebook,javascript,…)
  • Ne pas récupérer/gérer d’information des visiteurs (trackers,cookies,commentaires,…)
  • Faire le maximum de chose par moi même
  • Générer automatiquement les pages html

Code source

L’ensemble du code source de ce site se trouve sur mon dépôt github

Démarrage

Téléchargement

J’ai principalement travaillé avec la version 0.54.0, en récupérant l’exécutable sur le dépôt officiel des releases.

Lien direct vers la version Windows10 (en 64bit) ou la version Linux (en 64bit).

Découverte d’Hugo

Pour créer un premier site, vous pouvez suivre la documentation officielle. Les étapes sont bien expliquées et il existe un grand nombre de choix de theme pour commencer à manipuler l’outil et partir d’un visuel existant.

Structuration du site

Définition des répertoires du socle

1/documentation
2  +-- /archetypes
3  +-- /config
4  |   +-- /_default
5  +-- /content
6  +-- /ressources
7  +-- /themes
  • Le répertoire “archetypes” permet de stocker le template des articles de blogs
  • Le répertoire “config” permet de stocker les fichiers de configurations
  • Le répertoire “content” permet de stocker le contenant du site (articles du blog)
  • Le répertoire “ressources” permet de stocker l’ensemble des ressources générés (le site)
  • Le répertoire “themes” permet de stocker le theme utilisé

Définition des répertoires d’un thème

Les répertoires possibles pour un theme (en prenant exemple sur mon theme) :

 1/themes
 2  +-- /pragmatias
 3  |   +-- /i18n
 4  |   +-- /layouts
 5  |   |   +-- /_default
 6  |   |   +-- /partials
 7  |   |   +-- 404.html
 8  |   |   +-- index.html
 9  |   +-- /static
10  |   |   +-- /css
11  |   |   +-- /fonts
12  |   |   +-- /images
13  |   |   +-- /js
14  |   +-- LICENSE
15  |   +-- theme.toml

Le theme se nomme pragmatias et les metadonnées se trouvent dans le fichier theme.toml. Un exemple de contenu pour le fichier “theme.toml” peut être récupéré sur le site officiel.

Les répertoires importants sont :

  • i18n : permettant gérer la partie multi-langue (vocabulaire)
  • layouts : permettant de gérer l’affichage du site
  • static : permettant de stocker l’ensemble des ressources (js,fonts,css,images)

Définition du squelette du site

Création du fichier baseof.html dans le répertoire /themes/pragmatias/layouts/_default

 1<html lang="{{ with $.Site.LanguageCode }}{{ . }}{{ else }}fr{{ end }}">
 2    {{- partial "head.html" . -}}
 3    <body>
 4        {{- partial "header.html" . -}}
 5        <div id="content">
 6        {{- block "main" . }}{{- end }}
 7        </div>
 8        {{- partial "footer.html" . -}}
 9    </body>
10</html>

Cette page sera le squelette par défaut de chaque page du site.

Elle nécessite la création des fichiers suivants dans le répertoire /themes/pragmatias/layouts/partials :

  • head.html : permet de définir l’entête de chaque page HTML (les balises meta, javascript, css, etc …)
  • header.html : permet de définir le menu de navigation en haut du site
  • footer.html : permet de définir le pied de page du site

Pour l’entête et le pied de page, l’invocation des fichiers *.html se fait en utilisant la syntaxe suivantes :

1{{- partial "*.html" . -}}

Pour le corps, l’invocation des fichiers *.html se fait de manière plus complexe. Vous trouverez les informations dans les chapitres concernants la création des différentes section/pages du site. (blog, tags, archives, …)

Gestion du menu de navigation

Afin de gérer un menu de navigation en fonction de la taille de l’écran (mobile/desktop), j’ai utilisé la librairie javascript bootstrap.

Les étapes de mise en place sont les suivantes :

1. Télécharger les fichiers javascript bootstrap,jquery et popper dans les répertoires /themes/pragmatias/static/js et /themes/pragmatias/static/css

  • js/bootstrap.min.js
  • js/bootstrap.min.js.map
  • js/jquery-3.4.0.slim.min.js
  • js/jquery-3.4.0.slim.min.js.map
  • js/popper.min.js
  • js/popper.min.js.map
  • css/bootstrap.min.css
  • css/bootstrap.min.css.map

2. Ajout des informations suivantes dans le fichier /themes/pragmatias/layouts/partial/head.html pour charger les fichiers Javascript et CSS nécessaires

1    <!-- Bootstrap CSS -->
2    <link rel="stylesheet" href="{{ .Site.BaseURL }}/css/bootstrap.min.css">
3
4    <!-- Optional JavaScript -->
5    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
6    <script src="{{ .Site.BaseURL }}/js/jquery-3.4.0.slim.min.js"></script>
7    <script src="{{ .Site.BaseURL }}/js/popper.min.js"></script>
8    <script src="{{ .Site.BaseURL }}/js/bootstrap.bundle.min.js"></script>

3. Création du menu de navigation /themes/pragmatias/layouts/partial/header.html

 1	<div class="navbar navbar-expand-md prag-bg-primary prag-header" role="navigation">
 2        <div class="container-fluid  justify-content-center">
 3
 4            <button class="navbar-toggler justify-content-end prag-navbar" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
 5                <span class="navbar-toggler-icon"></span>
 6            </button>
 7
 8            <div class="collapse navbar-collapse" id="navbarSupportedContent">
 9                <ul class="navbar-nav text-center">
10                    {{ $currentPage := . }}
11                    {{ range .Site.Menus.global }}
12                    <li class="nav-item">
13                        <a class="nav-link" href="{{ .URL | absLangURL }}">{{ .Name }}</a>
14                    </li>
15                    {{ end }}
16                </ul>
17            </div>
18
19        </div>
20    </div>

4. Création des entrées dans le menu global, en ajoutant les informations suivantes dans le fichier de configuration principal config\_default\config.toml

1[[menus]]
2	[[menu.global]]
3		name = "Test"
4		weight = 1
5		identifier = "test"
6		url = "/test"

Définition d’une section Blog

Afin de pouvoir créer des articles, il faut mettre en place la gestion de la section Blog.

1. Création d’une entrée blog dans le menu global du fichier de configuration principal config\_default\config.toml

1	[[menu.global]]
2		name = "Blog"
3		weight = 1
4		identifier = "blog"
5		url = "/blog"

2. Création du répertoire /content/blog

Ce répertoire sera utilisé pour stocker l’ensemble des articles de la section Blog sous forme de fichier *.md.

Exemple du contenu d’un fichier *.md définissant un article :

1---
2Categories : [""]
3Tags : [""]
4title : "Premier article avec hugo"
5date : 2019-01-01
6draft : false
7---
8
9Welcome !

3. Création du fichier /themes/pragmatias/layouts/blog/list.html

Ce fichier sera appelé par défaut par le fichier baseof.html lorsque l’url /blog sera appelé.

 1{{ define "main" }}
 2<div class="container">
 3	{{ range (where .Site.Pages "Type" "blog") }}
 4	<article class="list-blog-post">
 5	    <header>
 6	        <h2 class="list-blog-post-title">
 7	            <a href="{{ .RelPermalink }}">{{ .Title }}</a>
 8	        </h2>
 9	    </header>
10	    <div class="list-blog-post-summary">
11	        {{ .Summary | safeHTML }}
12	    </div>
13	</article>
14	{{ end}}
15</div>
16{{ end }}

Remarque : le fait d’utiliser la syntaxe {{ define "main" }} ... {{ end }} aura pour effet de remplacer le block {{- block "main" . }}{{- end }} du fichier baseof.html

4. Création du fichier /themes/pragmatias/layouts/blog/single.html

Ce fichier sera appelé par défaut par le fichier baseof.html lorsque l’url /blog/titre_article.html sera appelé pour afficher le contenu d’un article. Un article étant défini par le contenu d’un fichier *.md du répertoire /content/blog

 1{{ define "main" }}
 2<div class="container">
 3	<article class="blog-post">
 4	    <header>
 5	        <h2 class="blog-post-title">
 6	            {{ .Title }}
 7	        </h2>
 8	    </header>
 9	    <main class="blog-post-main">
10	    	{{ .Content }}
11	  	</main>
12	</article>
13</div>
14{{ end }}

Définition d’une section Tags

Pour pouvoir lister les articles par Tags avec Hugo.

1. Ajouter une entrée Tags dans le menu global du fichier de configuration principal config\_default\config.toml

1	[[menu.global]]
2		name = "Tags"
3		weight = 2
4		identifier = "tags"
5		url = "/tags"

2. Ajouter le parametre suivant dans le fichier de configuration principal config\_default\config.toml pour activer la gestion des Tags

1[taxonomies]
2  tag = "tags"

3. Ajouter dans les fichiers *.md du répertoire content/blog le paramètre Tags avec les mots clés souhaités

1---
2Tags : ["Tags1","Tags2"]
3---

4. Création du fichier /themes/pragmatias/layouts/_default/terms.html pour gérer l’affichage de l’url /Tags

 1{{ define "main" }}
 2<div class="container prag-tags">
 3  {{ $type := .Type }}
 4  {{ range $key, $value := .Data.Terms.ByCount }}
 5    {{ $name := .Name }}
 6    {{ $count := .Count }}
 7    {{ with $.Site.GetPage (printf "/%s/%s" $type $name) }}
 8    <p>
 9      <a class="" href="{{ .Permalink }}">
10        {{ $name | humanize }}
11      </a>
12    </p>
13    {{ end }}
14  {{ end }}
15</div>
16{{ end }}

5. Création du fichier /themes/pragmatias/layouts/_default/taxonomy.html pour gérer l’affichage du contenu d’un Tags (liste des articles)

 1{{ define "main" }}
 2<div class="container">
 3  {{ range (where .Site.Pages "Type" "blog") }}
 4  <article class="list-blog-post">
 5      <header>
 6          <h2 class="list-blog-post-title">
 7              <a href="{{ .RelPermalink }}">{{ .Title }}</a>
 8          </h2>
 9      </header>
10      <div class="list-blog-post-summary">
11          {{ .Summary | safeHTML }}
12      </div>
13  </article>
14  {{ end}}
15</div>
16{{ end }}

Définition d’une section Archives

Afin de pouvoir lister les articles par année, il faut créer une section Archives.

1. Ajouter une entrée Archives dans le menu global du fichier de configuration principal config\_default\config.toml

1[[menus]]
2	[[menu.global]]
3		name = "Archives"
4		weight = 3
5		identifier = "archives"
6		url = "/archives"

2. Création du répertoire content\archives et d’un fichier _index.md avec le contenu suivant

1---
2Title : "Archives"
3date : 2019-01-01
4---

3. Création du fichier /themes/pragmatias/layouts/_default/list.html pour gérer l’affichage de l’url /Archives

 1{{ define "main" }}
 2<div class="container">
 3    {{ $v1 := where .Site.RegularPages "Type" "blog" }}
 4    {{ range ($v1.GroupByDate "2006") }}
 5    <div class="arch-posts-group">
 6        <div class="arch-post-year">{{ .Key }}</div>
 7        <ul class="arch-post-list">
 8            {{ range (where .Pages "Type" "blog") }}
 9            <li class="arch-post-item">
10                <a href="{{ .RelPermalink }}">
11                    <span class="arch-post-title">{{ .Title }}</span>
12                </a>
13            </li>
14            {{ end }}
15        </ul>
16    </div>
17    {{ end }}
18</div>
19{{ end }}

Mise en place du multilingue

Mise en place de deux langues (fr/en)

1. Définir la langue par défaut en ajoutant les paramètres suivants au début du fichier de configuration principal /config/_default/config.toml

1languageCode = "en"
2DefaultContentLanguage = "en"

2. Définir les différentes langues en ajoutant les paramètres suivants à la fin du fichier de configuration principal /config/_default/config.toml

 1[languages]
 2  [languages.en]
 3    weight = 10
 4    languageName = "English"
 5    contentDir = "content/en"
 6    languageCode = "en"
 7    [languages.en.params]
 8    imagePNG = "united-kingdom-flag-round-xs.png"
 9
10  [languages.fr]
11      weight = 20
12      languageName = "Français"
13      contentDir = "content/fr"
14      languageCode = "fr"
15      [languages.fr.params]
16      imagePNG = "france-flag-round-xs.png"

3. Définition du dictionnaire anglais en créant le fichier en.toml dans le répertoire /themes/pragmatias/i18n

1[more_read]
2other = "Read more"

4. Définition du dictionnaire français en créant le fichier fr.toml dans le répertoire /themes/pragmatias/i18n

1[more_read]
2other = "Continer la lecture"

5. Pour utiliser les termes des dictionnaries dans les fichiers html du répertoire /themes/pragmatias/layouts, il faut utiliser la syntaxe suivante :

1{{ i18n "more_read" }}

Gestion des mois en fonction de la langue

1. Ajouter la traduction des 12 mois dans le dictionnaire Anglais /themes/pragmatias/i18n/en.toml

 1[January]
 2other = "January"
 3[February]
 4other = "February"
 5[March]
 6other = "March"
 7[April]
 8other = "April"
 9[May]
10other = "May"
11[June]
12other = "June"
13[July]
14other = "July"
15[August]
16other = "August"
17[September]
18other = "September"
19[October]
20other = "October"
21[November]
22other = "November"
23[December]
24other = "December"

2. Ajouter la traduction des 12 mois dans le dictionnaire Français /themes/pragmatias/i18n/fr.toml

 1[January]
 2other = "Janvier"
 3[February]
 4other = "Février"
 5[March]
 6other = "Mars"
 7[April]
 8other = "Avril"
 9[May]
10other = "Mai"
11[June]
12other = "Juin"
13[July]
14other = "Juillet"
15[August]
16other = "Aout"
17[September]
18other = "Septembre"
19[October]
20other = "Octobre"
21[November]
22other = "Novembre"
23[December]
24other = "Décembre"

3. Utiliser la syntaxe suivante pour afficher les dates dans les fichiers html du répertoire /themes/pragmatias/layouts

1{{.Date.Day}} {{i18n .Date.Month}} {{.Date.Year}}

Ajout d’un flux RSS concernant la section Blog

1. Ajout des informations de sortie dans le fichier de configuration principal /config/_default/config.toml

1[outputs]
2  home = [ "HTML", "RSS" ]
3  section = [ "HTML"] 
4  taxonomy = [ "HTML"]

2. Création de la définition du flux RSS dans le fichier /themes/pragmatias/layouts/_default/rss.xml

 1<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 2  <channel>
 3    <title>{{ .Site.Title }}</title>
 4    <link>{{ "/blog" | absLangURL }}</link>
 5    <description>{{ i18n "rss_content" }} {{ .Site.Title }}</description>
 6    <generator>Hugo {{ .Hugo.Version }}</generator>
 7    {{ with .Site.LanguageCode }}
 8      <language>{{.}}</language>
 9    {{end}}
10    {{ with $.Site.Author.name }}
11      <webMaster>{{.}}</webMaster>
12    {{end}}
13    {{ with .Site.Copyright }}
14      <copyright>{{.}}</copyright>
15    {{end}}
16    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>
17    <atom:link href="{{ "/feed.xml" | absLangURL }}" rel="self" type="application/rss+xml" />
18    {{ range first 10 (where .Data.Pages "Type" "blog") }}
19    <item>
20      <title>{{ .Title }}</title>
21      <link>{{ .Permalink }}</link>
22      <pubDate>
23          {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}
24      </pubDate>
25      <lastModDate>
26          {{ .Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}
27      </lastModDate>
28      <guid>{{ .Permalink }}</guid>
29      <description>{{ .Summary | html }}</description>
30    </item>
31    {{ end }}
32  </channel>
33</rss>

3. Ajout des informations dans le fichier /themes/pragmatias/layouts/partials/head.html :

1    <!-- Flux RSS -->
2    <link rel="alternate" type="application/rss+xml" href="{{ "/feed.xml" | absLangURL }}" />

4. Ajout de l’icone RSS dans le fichier /themes/pragmatias/layouts/partials/header.html :

1   <li class="nav-item">
2      <a href="{{ "/feed.xml" | absLangURL }}" title="{{ .Site.Title}}" class="prag_header_svg mr-1 ml-1">
3        {{ partial "svg/social-rss-circle-internet.svg" (dict "size" "24px") }}
4      </a>
5    </li>

Gestion du fichier CSS principal

Utilisation d’un fichier CSS

1. Creer le fichier CSS souhaité dans le répertoire /themes/pragmatias/static/css

1html {}
2
3body {}

2. Ajouter l’utilisation du fichier CSS dans le fichier /themes/pragmatias/layouts/partials/head.html

1    <link rel="stylesheet" href="{{ .Site.BaseURL }}/css/pragmatias.css">

Gestion des couleurs

Pour pouvoir gérer les couleurs de l’ensemble du site à un seul endroit.

1. Définir l’ensemble des variables nécessaire au début du fichier CSS

1:root {
2  --color-bg-primary:#fdfaee;
3  --color-fg-head-titre:#03416d;
4}

2. Utiliser la syntaxe suivante pour appeler la variable définissant la couleur attendue

1body {
2  background-color: var(--color-bg-primary);
3  color: var(--color-fg-blog-texte);
4}

Gestion des fonts

Pour pouvoir utiliser une font spécifique sans passer par un webservice externe.

1. Copier les fichiers de la font choisie au format woff et woff2 dans le répertoire /themes/pragmatias/static/fonts

  • OpenSans-Regular.woff
  • OpenSans-Regular.woff2
  • OpenSans-Bold.woff
  • OpenSans-Bold.woff2
  • OpenSans-Italic.woff
  • OpenSans-Italic.woff2
  • OpenSans-BoldItalic.woff
  • OpenSans-BoldItalic.woff2

2. Définir les fonts pouvant être utiliser dans le CSS

 1@font-face {
 2  font-family: 'OpenSans';
 3  src: url('/fonts/OpenSans-Regular.woff2') format('woff2'), url('/fonts/OpenSans-Regular.woff') format('woff');
 4  font-weight: 400;
 5  font-style: normal;
 6}
 7
 8@font-face {
 9  font-family: 'OpenSans';
10  src: url('/fonts/OpenSans-Bold.woff2') format('woff2'), url('/fonts/OpenSans-Bold.woff') format('woff');
11  font-weight: 700;
12  font-style: normal;
13}
14
15@font-face {
16  font-family: 'OpenSans';
17  src: url('/fonts/OpenSans-Italic.woff2') format('woff2'), url('/fonts/OpenSans-Italic.woff') format('woff');
18  font-weight: 400;
19  font-style: italic;
20}
21
22@font-face {
23  font-family: 'OpenSans';
24  src: url('/fonts/OpenSans-BoldItalic.woff2') format('woff2'), url('/fonts/OpenSans-BoldItalic.woff') format('woff');
25  font-weight: 700;
26  font-style: italic;
27}

3. Utiliser le nom de la font dans les propriétés du CSS

1body {
2  font-family: 'OpenSans';
3}