Comment Creer Un Site Avec Hugo Partie 5 : Le Template Des Pages

Aldok

| 10 minutes

Revenir à l'index du tuto : Creer un site avec hugo

Créer un site avec Hugo partie 5 : le template des pages

Le template par défaut de la page de contenus

Si vous jetez un oeil dans le template layouts/_defaults/single.html, vous verrez qu'il est vide : c'est le fichier par défaut que Hugo va aller chercher s'il ne trouve rien qui ne corresponde mieux au template d'une page simple.

Comme on l'a fait pour les autres templates, on va donc lui ajouter un peu de contenu par défaut :

<section class="section">
	<div class="container max-800px">
		<h1 class="title is-3">
			{{ .Title }}
		</h1>
		<div class="content">
			{{ .Content }}
		</div>
	</div>
</section>
{{ end }}

Avec ce template léger, toutes les pages vont se calquer sur la largeur max de 800px commune aux autres templates que nous avons déjà fait.

La page " A propos”

Nous allons maintenant créer une page “A propos” qui va afficher un petit laïus sur les objectifs du site, comme on en voit souvent, simplement en entrant la commande suivante :

hugo new a-propos.md

Revenons maintenant sur le layout a-propos.html

Pour y intégrer du contenu, on doit ajouter les boucles habituelles :

{{ define "main" }}
<div class="section">
    <div class="container max-800px">
        <div class="content">
            {{ .Content }}
        </div>
    </div>
</div>
{{ end }}

Mais on ne va pas s'arrêter en si bon chemin : pour suivre la tendance actuelle, on va lui ajouter un hero header (et puisque c'est mignon et que je ne suis pas particulièrement inspiré au moment d'écrire ces lignes, on va mettre une image de chat).

Le résultat devrait être celui-ci :

Exemple de page apercu

Pour arriver à cette fin :

- Choisissez l'image qui servira d'illustration, et uploadez-la dans le dossier /static/images/ (chez moi, elle s'appelle chat.jpg)

- Dans votre fichier template a-propos.html, ajoutez la mise en forme du hero header suivante, juste au dessus de la section qui affiche le contenu :

<div class="hero is-dark is-fullheight-with-navbar" {{ with .Params.heroimage }}
    style="background: url({{ . }}) center top; background-size:cover;" {{ end }}>
    <div class="hero-body">
        <div class="container max-800px">
            <h1 class="title is-1 has-background-primary narrow-background">
                {{ .Title }}
            </h1>
            {{ with .Params.herotext }}
            <h2 class="title subtitle is-4 has-background-primary narrow-background">
                {{ . }}
            </h2>
            {{ end }}
        </div>
    </div>
</div>

Puis ajoutez ceci à votre front matter dans le fichier a-propos.md :

heroimage: /images/chat.jpg
herotext: Je m'appelle Whisper, pour vous servir !

Quelques explications s'imposent :

Là encore, le template va s'appuyer sur les infos écrites dans le front matter de l'article ;

- La boucle {{ with .Params.heroimage }} … {{ end }} va permettre d'entrer dans la condition “si le paramètre heroimage est renseigné dans le frontmatter”. Si c'est le cas, alors le background ira chercher l'image qui correspond au nom renseigné dans le front matter ( via l'appel “background: url({{ . }}) …" ) . Donc ici, il ira chercher /images/chat.jpg vu que c'est la valeur indiquée dans le front matter.

- Le {{ .Title }} va tout simplement récupérer le titre de la page tel qu'il est renseigné dans le front matter, comme d'habitude.

- La boucle {{ with .Params.herotext }} … {{ . }} … {{ end }} va quant à elle, comme vous le devinez, récupérer la valeur renseignée à herotext dans le frontmatter.

Pour ce qui est de la mise en forme, on applique simplement une classe hero (encore propre à Bulma), mais pour fixer correctement le contenu textuel (titre de la page et texte du heroheader) il va falloir faire une petite manip en CSS.

Ajoutez simplement ceci à votre fichier style.css :

.narrow-background {
display: table;
padding: 0.1em;
}

Ce n'est peut être pas la façon la plus propre de faire, mais je ne vais pas m'attarder sur les éléments html / css : là encore ce n'est pas l'objet principal du tuto !

Voilà donc, à ce stade, à quoi doit ressembler la page de contenu a-propos.md (avec un peu de contenus aléatoires)

---
title: "A Propos"
date: 2020-04-08T11:15:50+02:00
draft: false
layout: a-propos
heroimage: /images/chat.jpg
herotext: Je m'appelle Whisper, pour vous servir !
---

## A propos de ce super site

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris et urna a odio consectetur sollicitudin id eu massa. Donec ante eros, dignissim ut iaculis vel, dignissim in eros. Aliquam massa elit, viverra nec eros sit amet, dapibus suscipit nibh. Aliquam egestas congue nulla et lobortis. Curabitur pellentesque dignissim neque ac egestas. Etiam eu risus non est scelerisque cursus. Nullam ullamcorper massa nec rutrum placerat. Aenean dignissim mi vel tellus ornare, in commodo tellus ultricies. Cras posuere turpis ante, non mollis lectus molestie ac. Donec vel mauris at tellus fermentum viverra in id turpis. Nulla sed enim nunc.

Aenean scelerisque sem a nulla iaculis, a tempus nulla faucibus. Phasellus fermentum nisl vehicula sollicitudin varius. Pellentesque eros lectus, dignissim vitae accumsan vel, ullamcorper eget metus. Nam eget fermentum magna. Morbi viverra egestas felis quis viverra. Donec pretium ipsum sapien, et scelerisque ipsum aliquet ut. Sed euismod ligula varius ligula suscipit tincidunt. Fusce vitae dignissim urna. Fusce bibendum porttitor ante vitae sodales. Duis erat est, mattis quis risus id, fermentum accumsan ipsum. Cras pharetra posuere dolor, sed finibus enim fermentum nec.

et la page de template correspondante a-propos.html :

{{ define "main" }}

<div class="hero is-dark is-fullheight-with-navbar" {{ with .Params.heroimage }}
    style="background: url({{ . }}) center top; background-size:cover;" {{ end }}>
    <div class="hero-body">
        <div class="container max-800px">
            <h1 class="title is-1 has-background-primary narrow-background">
                {{ .Title }}
            </h1>
            {{ with .Params.herotext }}
            <h2 class="title subtitle is-4 has-background-primary narrow-background">
                {{ . }}
            </h2>
            {{ end }}
        </div>
    </div>
</div>

<div class="section">
    <div class="container max-800px">
        <div class="content">
            {{ .Content }}
        </div>
    </div>
</div>

{{ end }}

Libre à vous d'adapter le html / css comme bon vous semble : le principal, c'est d'avoir compris le fonctionnement des boucles qui appellent les données du fichier markdown !

Création d'une page Contact

Pour la page contact, on va partir sur un layout un peu similaire :

  1. Ajouter une image dans votre dossier d'images, qui servira d'illustration dans le hero header (pour ma part j'ai utiliser un fichier appelé “contact.jpg”

  2. Créer la page layouts/_defaults/contact.html

Contenu de la page contact.html :

{{ define "main" }}

<div class="hero is-dark is-medium" {{ with .Params.heroimage }}
    style="background: url({{ . }}) center center; background-size:cover;" {{ end }}>
    <div class="hero-head">
        <div class="container">
            <h1 class="title is-2 has-background-primary narrow-background">
                {{ .Title }}
            </h1>
            {{ with .Params.herotext }}
            <h2 class="title subtitle is-4 has-background-primary narrow-background">
                {{ . }}
            </h2>
            {{ end }}
        </div>
    </div>
    <div class="hero-body"></div>
    <div class="hero-foot">
        {{ with .Params.buttons }}
        <nav class="tabs">
            <div class="container">
                <ul>
                    {{ range . }}
                    <li>
                        <a class="has-background-primary" {{ with .id }}id="{{ . }}" {{ end }}
                            {{ with .href }}href="{{ . | safeURL }}" {{ end }}>
                            <span class="icon"><i class="{{ .icon }}" aria-hidden="true"></i></span>
                            <span>{{ .name }}</span>
                        </a>
                    </li>
                    {{ end }}
                </ul>
            </div>
        </nav>
        {{ end }}
    </div>
</div>

<div class="section">
    <div class="container">
        <div class="columns">
            <div class="column">
                <div class="content">
                    {{ .Content }}
                </div>
            </div>
            {{ with .Params.map }}
            <div class="column is-one-third">
                <div class="map-responsive">
                    <iframe
                        src="https://maps.google.com/maps?hl=en&amp;q={{ . | htmlEscape }}&amp;ie=UTF8&amp;t=&amp;z=15&amp;iwloc=B&amp;output=embed"
                        frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
                </div>
            </div>
            {{ end }}
        </div>
    </div>
</div>

{{ end }}
  1. Ensuite, on créé la page de contenu en markdown, en précisant le nom du layout et de l'image qui servira d'illustration :
hugo new contact.md

Contenu de la page contact.md :

---
title: "Contact"
date: 2020-04-09T16:00:02+02:00
layout: contact
heroimage: /images/contact.jpg
---

## Me Contacter
  1. Et on ajoute l'élément de menu dans le fichier de configuration config.toml pour créer le lien vers la page contact dans le menu principal du site :
[[menu.main]]
    name = "Contact"
    url = "/contact"

Quelques commentaires :

Pas besoin de revenir sur le Hero Header et son image de fond, vous savez faire maintenant !

Comme vous pouvez le voir dans le template, il y a une section “hero footer” qui contient quelques onglets dont le contenu est appelé dynamiquement depuis le front matter du fichier markdown contact.md

Ce contenu est appelé avec la boucle {{ .with .Params.buttons }} ; maintenant que vous avez compris un peu le fonctionnement du langage de templates, vous comprenez qu'il va nous chercher les valeurs à un truc qu'on appelle “buttons” dans le front matter.

On voit aussi que ces “buttons” on des propriétés, qui sont appelées .href, .icon, .id et .name.

Ajoutons donc ces valeurs dans le front matter de contact.md :

buttons:
- name: Phone
icon: fas fa-phone
href: "tel:555555555"

- name: Contact form
icon: fas fa-envelope
id: open-modal

Chaque occurence inscrite rajoutera automatiquement un bouton au pied de notre header, avec les valeurs renseignées !

Pour savoir quelle icône utiliser, il suffit de jeter un oeil à la bibliothèque de font awesome : https://fontawesome.com/

Le seule petit hic ici, c'est que nous utilisons des onglets qui ne sont pas responsives ; pour remédier à cela, Bulma propose une solution : ajouter ceci dans le style de la page (avant le hero header) ->

<style>
    @media screen and (max-width: 768px) {
        li>a>span:not(.icon) {
            visibility: hidden;
            position: absolute;
        }
        nav.tabs li {
            -webkit-box-flex: 0;
            -ms-flex-positive: 0;
            flex-grow: 0;
            -ms-flex-negative: 1;
            flex-shrink: 1;
        }
        .tabs .icon {
            margin-left: 0.5em;
        }
    }
</style>

Le contenu de la page contact, et l'affichage d'une carte

Le code qui suit doit être ajouté après le hero-header, pour appeler une carte Google Maps :

<div class="section">
    <div class="container">
        <div class="columns">
            <div class="column">
                <div class="content">
                    {{ .Content }}
                </div>
            </div>
            {{ with .Params.map }}
            <div class="column is-one-third">
                <div class="map-responsive">
                    <iframe
                        src="https://maps.google.com/maps?hl=en&amp;q={{ . | htmlEscape }}&amp;ie=UTF8&amp;t=&amp;z=15&amp;iwloc=B&amp;output=embed"
                        frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
                </div>
            </div>
            {{ end }}
        </div>
    </div>
</div>

Il suffit donc d'ajouter la variable map et lui affecter une valeur dans le front matter :

map: Rouen, France

Pour que la carte s'affiche bien en mode responsive, on peut également ajouter ce style comme l'auteur original de l'article le propose :

.map-responsive {
	overflow: hidden;
	padding-bottom: 100%;
	position: relative;
	height: 0;
}

.map-responsive iframe {
	left: 0;
	top: 0;
	height: 100%;
	width: 100%;
	position: absolute;
    }

L'ajout du formulaire de contact

Dernière partie et pas des moindre : ajouter un formulaire de contact à la page. Enfin un élément d'interaction ! Mais comment faire en HTML me demanderez-vous ? C'est ce que nous allons voir…

Comme nous n'avons pas vraiment de serveur actif pour gérer l'envoi de mail, il va falloir passer par un service tiers : c'est ce qui fait à la fois la force et la faiblesse d'un site en Jamstack. On peut soit utiliser Netlify (le service qui nous permettra de déployer notre site) soit passer par un service qui propose la création de formulaires, comme Google Forms.

Pour se simplifier le travail, on va choisir ce dernier.

Nous avons déjà ajouté un bouton pour ouvrir le formulaire de contact avec l'id modal-form tel qu'il est définit dans le front matter.

Voilà le code qui permet d'ajouter un formulaire Google sur la page :

{{ with .Params.form }}
<style>
    .contact-form {
        width: 640px;
        height: 960px;
        overflow: scroll;
    }
</style>
<div class="modal">
    <div class="modal-background"></div>
    <div class="modal-content" style="overflow: auto!important; -webkit-overflow-scrolling: touch!important;">
        <iframe class="contact-form" src="{{ . }}" width="640" height="1019" frameborder="0" marginheight="0"
            marginwidth="0">Loading...</iframe>
    </div>
    <button class="modal-close is-large" aria-label="close"></button>
</div>
<script>
    document.querySelector('a#open-modal').addEventListener('click', function (event) {
        event.preventDefault();
        var modal = document.querySelector('.modal');
        var html = document.querySelector('html');
        modal.classList.add('is-active');
        html.classList.add('is-clipped');


        var close = (e) => {
            e.preventDefault();
            modal.classList.remove('is-active');
            html.classList.remove('is-clipped');
        }
        modal.querySelector('.modal-background').addEventListener('click', close);
        modal.querySelector('.modal-close').addEventListener('click', close);
    });
</script>
{{ end }}

Comme vous pourrez le constater en détaillant le code, on va utiliser une boucle {{ with .Param.form }} qui va n'avoir qu'un seul paramètre : la source de l'iframe qui va aller chercher le formulaire.

Il suffit alors de créer un formulaire Google depuis votre compte Google Drive, et de récupérer son URL.

Une fois l'url récupérée, il suffit de la renseigner dans le front matter avec cette ligne :

form: https://docs.google.com/forms/d/e/1314214124124124_etc.

Pour ce qui est du code, si vous faites un essai, vous verrez qu'il utilise Bulma et du jQuery pour afficher un élément dans une pop-up simple & efficace ; je ne vais pas rentrer dans le détail, encore une fois libre à vous d'intégrer le formulaire comme vous le voulez !

Le hic avec cette méthode, c'est que les e-mails envoyés ne sont visibles que dans les réponses au formulaire Google par défaut… On pourra améliorer ça plus tard avec un autre script pour gérer cet envoi d'e-mail.

En conclusion

Nous avons fait le tour des quelques fonctionnalités qui permettent de créer des pages avec des templates spécifiques. A cette étape vous devriez commencer à beaucoup mieux comprendre le fonctionnement de Hugo : nous allons à présent voir comment gérer l'apparence des listes et des taxonomies !

Vous devriez également aimer ce qui suit...