Maintenant que l'en-tête de mes entrées de journal est structurée, il est possible de l'enrichir avec plus d'informations.
Temps de lecture
Je vais commencer par ajouter un temps de lecture. Pour éviter d'avoir à le saisir à la main pour chaque entrée[1], j'installe un plugin spécialement conçu pour eleventy : eleventy-plugin-time-to-read
Lien vers la documentation du plugin(S'ouvre dans un nouvelle fenêtre)
Par défaut, le plugin calcule le temps de lecture sur la base du nombre de caractère dans mon contenu (1000 caractères = 1 minute de lecture), mais je ne suis pas sûr que ce soit la méthode de calcule la plus fiable (un lecteur "à l'aise" va plutôt identifier les mots globalement et uniquement déchiffrer lettre par lettre les mots inconnus).
Après une (très) rapide recherche sans grande rigueur scientifique[2], il semble que la vitesse moyenne de lecture soit aux alentours de 230 mots à la minute. C'est donc ainsi que j'ai paramétré le plugin en l'ajoutant dans mon fichier de configuration :
import timeToRead from "eleventy-plugin-time-to-read";
export default function(eleventyConfig) {
eleventyConfig.addPlugin(timeToRead), {
speed: '230 words per minute',
language: 'fr',
seconds: false
};
}
Note : J'ai globalement défini la langue en français. Ce sera peut-être un problème si je décide d'écrire des articles en anglais ou à proposer une version anglaise du blog, mais je verrais à ce moment-là.
Il ne reste plus qu'à insérer ce temps de lecture dans mon template des entrées de journal. Rien de bien compliqué ici : je passe le contenu de mon article à la fonction et elle me renvoie le temps de lecture estimé :
<p class="reading-time">Temps de lecture : <span aria-label="environ">~</span> {{ content | timeToRead }}</p>
Note - Remarque sur Liquid : La façon dont Liquid s'écrit peut un peu surprendre. Ici, on a un peu l'impression d'injecter deux fois le contenu de notre entrée (dans le temps de lecture et plus bas, dans le corps de l'entrée) et on peut s'inquiéter de doubler inutilement la taille de la page. Mais il faut bien comprendre que le contenu ne sera en fait pas ajouté dans le HTML final deux fois. En fait le contenu va être fourni au filtre sous forme d'argument pour la fonction timeToRead() que ce filtre va exécuter. Au final, seul le résultat de l'opération sera intégré dans le HTML final.
Note - Accessibilité : Je tenais à préciser que le temps affiché était une estimation mais je ne voulais pas que la ligne soit trop longue, j'ai donc utilisé le symbole tilde (~) qui peut être utilisé en mathématique pour indiquer que le résultat est approximatif. J'ai été tenté de le masquer aux lecteurs d"crans et d'ajouter à la place "environ", sous forme de texte masqué, car à l'oral c'est ainsi que je le lirais. Mais j'ai fini par le laisser tel quel, craignant de sur-complexifier une annotation qui est, je suppose, assez répandue pour être comprise.
Enfin, il faut que je positionne ce temps de lecture dans l'en-tête. Par défaut, avec toute la réorganisation que j'ai faite, il s'affiche tout en haut, ce qui n'a pas trop de sens. Je préférerais qu'il soit en bas, entre le h1 et le début de l'entrée. Pour ça ,j'ajoute une petite règle CSS pour que tous les contenus ajoutés dans l'en-tête soient par défaut après le h1 (sauf si spécifié autrement) :
body.log-entry #article-header > * {
order: 3;
}
Améliorer la date de publication
J'ai déjà donné une "jolie"[3] apparence à ma date mais, sous le capot, c'est toujours un bête paragraphe. Pas très utile pour les robots (comme les robots d'indexation, par exemple). Si je remplaçais mon paragraphe par une balise <time>, cela aiderait ces robots à hiérarchiser mes entrées de journal.
- Voir la doc de la balise
<time>(S'ouvre dans un nouvelle fenêtre) - Voir la doc de la balise
<article>(S'ouvre dans un nouvelle fenêtre), qui explique ce que signifie une balise<time>dans ce contexte.
Changer d'un paragraphe à une date ne pose pas trop de problème, par contre remplir l'attribut datetime me pose un petit souci : par défaut la date renvoyé en javascript est extrêmement longue et ne semble pas correspondre aux format valides décrits dans la documentation plus haut. Par exemple, pour cette entrée j'obtiens la chaîne de caractère suivante, avec un étrange mélange d'anglais et de français :
Tue Dec 30 2025 17:16:33 GMT+0100 (heure normale d’Europe centrale)
Pour solutionner cela, je créé un nouveau filtre pour générer une date formatée pour les machines, toujours grâce à la librairie Luxon, avec la méthode toISO et j'obtiens :
2025-12-30T16:16:33.567Z
Bien mieux, mais j'ai quand même un petit problème : il y a un décalage d'une heure (en soi, ce n'est pas très grave, mais ça dérange mon sens de la précision). Le responsable est toujours cette implémentation récupérée du blog starter d'eleventy, qui force le très fictif[4] fuseau horaire universel (UTC) (voir le bout de code concerné dans l'entrée "Amélioration du journal : dates"). Pour corriger cela, deux possibilités :
- Simplement supprimer la possibilité de définir un fuseau horaire dans le filtre (dans ce cas, c'est le fuseau horaire de la machine sur laquelle je lance eleventy qui fait foi)
- Redéfinir le fuseau horaire par défaut sur celui de Paris
J'opte pour définir le fuseau horaire explicitement sur Paris car :
- Si jamais je me retrouve sur un fuseau horaire différent (en voyage par exemple) et que je souhaite re-générer le site, je ne souhaite pas changer soudainement les dates de toutes mes entrées
- Ça me me laisse la possibilité de générer des dates sur d'autres fuseaux horaires, si ça devait être nécessaire.
J'obtiens donc le filtre suivant :
eleventyConfig.addFilter("dateForRobots", (dateObj, zone) => {
return DateTime.fromJSDate(dateObj, { zone: zone || "Europe/Paris" }).toISO();
});
Et les dates générées pour les machines respectent enfin mon fuseau horaire :
2025-12-30T17:16:33.567+01:00
Note : le fait que j'ai utilisé Europe/Paris plutôt que utc+1 fait que le fuseau horaire s'adaptera automatiquement aux changement d'heure (car oui, pour l'instant les heures d'été et d'hiver restent une réalité en France).
Au passage, j'en profite pour modifier le filtre readableDate récupéré du starter blog eleventy : celui-ci utilise un format par tokens individuels, ce qui n'est pas recommandé dans la documentation de la bibliothèque Luxon. Celle-ci recommande l'usage de la méthode toLocaleString(S'ouvre dans un nouvelle fenêtre) et de presets internationalisés.
Ajouter l'auteur
Une autre information généralement attendue sur un article est le nom de l'auteur. Je n'en ai pas parlé lorsque j'ai ajouté la date de l'article, mais je constate que selon les sites, ces deux informations peuvent se retrouver en début ou en fin d'article. Personnellement, je déteste avoir à descendre en fin d'article pour savoir qui parle et quand. Je placerais donc l'auteur en haut d'article.
Indiquer aux robots l'auteur d'un article
Il ne suffit pas de placer mon nom dans un article pour que les robots d'indexation m'identifient comme son auteur. Pour ça il faudrait que j'utilise une balise sémantique, mais HTML 5 ne semble pas proposer de balise spécifique pour indiquer un auteur…
Je me rabat donc sur les microformats, et plus particulièrement le microformat h-entry(S'ouvre dans un nouvelle fenêtre) qui semble dédié aux articles de blog. L'implémentation est assez simple : il me "suffit"[5] d'ajouter une classe h-entry à la racine de mon entrées puis d'ajouter d'autres classes suivant la spécification du microformat que j'ai choisi d'utiliser, sans rien changer d'autre dans la structure de mon HTML. C'est ce que je fais sur les éléments clefs de mes entrées (h1, nom de l'auteur, date de publication, contenu de l'entrée, …) :
<header id="article-header">
…
<h1 class="p-name">Enrichir l'en-tête des entrées de journal</h1>
<time class="dt-published" datetime="2025-12-30T17:16:33.567+01:00">30 décembre 2025</time>
<p class="p-author">Guillaume Barbier</p>
…
</header>
<div id="article-content" class="e-content">
…
</div>
Note : J'ai aussi vu que les robots d'indexation se basaient sur des liens avec l'attribut rel="author". Il va falloir que je me créé une page de bio et que j'ajoute une lien… mais plus tard.
Dynamiser le nom de l'auteur
Il est tentant de simplement hardcoder mon nom, mais ce n'est pas très sérieux, d'autant plus qu'il est prévu que nous soyons deux à écrire sur ce blog (même si je resterais probablement le seul auteur de ce journal de bord) : il faut que je prévois un système qui permette de changer l'auteur d'un article.
Pour l'instant, je n'ai qu'un nom à afficher, mais je compte bien enrichir tout ça par la suite (un portrait, un lien vers une bio, vers les réseaux, etc.). Il va donc me falloir un système qui permette d'indiquer rapidement un auteur, sans avoir à fournir à chaque fois toutes ses informations.
Je choisi de procéder en trois étapes :
- Créer un registre d'auteur transverse dans lequel je pourrais aller chercher les informations relatives (pour l'instant, juste le nom)
- Une propriété simple et rapide à saisir pour indiquer l'auteur d'un article
- Une propriété dynamique pour remplacer la propriété saisie dans l'article par les données trouvées dans le registre.
1 - Le registre d'auteur
Ce registre est assez simple à mettre en place : je reprend mon fichier _data/base.js et je lui ajoute un objet très simple, avec trois clefs (une pour moi, une pour Myriam – ma future co-autrice – et une valeur par défaut) :
export default {
authors: {
guillaume: {
name: "Guillaume Barbier"
},
myriam: {
name: "Myriam Nguyen-The"
},
default: {
name: "Auteur inconnu"
}
}
}
2 - Définir rapidement un auteur
Grâce à mon registre, le simple prénom de l'auteur ou autrice devrait me suffire pour ajouter toutes ses informations dans la page (pour l'instant, juste le nom complet). Pour le nom de la propriété, j'opte pour l'anglais afin de rester cohérent avec les propriétés existante du front matter :
---
title: Enrichir l'en-tête des entrées de journal
author: guillaume
---
Comme je suis fainéant et n'ai pas envie de préciser l'auteur sur chaque entrée de mon journal, j'ajoute cette propriété à mon fichier /content/journal/journal.11tydata.js, ainsi elle s'appliquera à tous mes articles passés, présents et futurs (sauf si spécifié autrement dans l'article).
3 - La propriété dynamique pour récupérer les infos détaillées
C'est la dernière étape. le principe est assez simple : remplacer dynamiquement la donnée author par un object contenant les données de mon registre :
// _data/eleventy.config.js
export default {
author: (data) => data.base.authors[data.author]
}
Note : j'aurais pu créer un filtre à la place, qui renvoie la donnée demandée mais je trouvais la syntaxe un peu lourde en Liquid : {{ author | getAuthorData: name}}. L'usage des propriétés dynamique me permet d'user de la syntaxe objet, plus courte et plus familière : {{ author.name }}
Mais mon code simpliste ne prend pas en compte plusieurs erreurs qui pourraient se produire :
- Oublier de définir
authorsur un article (pas sur mes entrées de journal sur lesquelles je suis défini comme auteur par défaut) - Se tromper en saisissant un nom (en mettant des majuscules, en doublant des lettres, etc.)
- Saisir un auteur qui n'existe tout simplement pas
Pour éviter ces erreurs, il faut que je vérifie si l'auteur a été fourni et correspond à un auteur répertorié :
// _data/eleventy.config.js
export default {
author: (data) => {
if (data.author && Object.hasOwn(data.base.authors, data.author)) {
return data.base.authors[data.author.toLowerCase()]
} else {
return data.base.authors["default"]
}
}
Note : Pour éviter les erreurs de casse, j'ai ajouté une fonction pour passer les noms d'auteurs en minuscules.
Pour gérer les auteurs absents ou non reconnus, j'ai choisi de remplacer la donnée invalide par des données par défaut, définie dans mes données transverses (j'ai envisagé de renvoyer la donnée saisie en l'enrichissant avec les valeurs par défaut, mais ça compliquait le code et je n'y ai pas vu un intérêt immédiat).
Mais "Auteur inconnu" n'est pas vraiment ce que j'ai envie d'afficher dans mes articles, donc j'ai essayé d'ajouter un warning quand un article est généré sans nom d'auteur… sauf que eleventy génère ses pages en plusieurs passes et les propriétés dynamiques sont appelées à chaque fois. Par conséquent, le cas "auteur non défini" arrive au moins une fois à la génération de chaque page.
Pour contourner ce problème, j'ai créé une sorte "d'enregistreur" pour mes pages articles :
- à chaque passe sur un article, si la page n'a pas – à ce moment-là – d'auteur défini, j'appelle une fonction pour enregistrer une alerte (si une alerte n'existe pas déjà)
- l'alerte est définie avec un délais (de 3 secondes, la génération s'exécutant en quelques millisecondes)
- si lors d'une repasse sur une page un auteur est cette fois-ci défini, l'alerte est supprimée.
const articleWithoutAuthorLogger = {}
function warningNoAuthor (pageSlug) {
if(articleWithoutAuthorLogger[pageSlug]) {
// Ne rien faire, une alerte a déjà été créée pour cette page.
} else {
articleWithoutAuthorLogger[pageSlug] = setTimeout(
() => console.warn('(!) No valid author specified. Please add one for this article: ' + pageSlug),
3000
)
}
}
export default {
author: (data) => {
if (data.author && Object.hasOwn(data.base.authors, data.author)) {
if(data.contentType == "article" && articleWithoutAuthorLogger[data.page.fileSlug])
clearTimeout(articleWithoutAuthorLogger[data.page.fileSlug]);
return data.base.authors[data.author.toLowerCase()]
} else {
if(data.contentType == "article") warningNoAuthor(data.page.fileSlug);
return data.base.authors["default"]
}
}
}
Je peux maintenant très rapidement définir un auteur / une autrice pour un article et facilement étendre les données disponibles pour auteurs et autrices en fonction des besoins à venir.
Et puis sur quelle base estimer ce temps de lecture ? Une mesure de mon propre temps de lecture ? Outre l'aspect fastidieux de la tâche, je ne pense pas que ce soit une mesure très objective… ↩︎
J'ai pris les premières réponses que je trouvais, sans approfondir mes recherches, vérifier les sources et chercher de possibles contradictions. ↩︎
Oui, le "joli", c'est subjectif ↩︎
La documentation des fuseaux horaires en javascript(S'ouvre dans un nouvelle fenêtre) met en garde contre l'usage du fuseau "UTC" ↩︎
Ajouter une classe sur la balise
<article>n'est pas si simple. En effet, celle-ci est gérée par mon template "base". Il faut donc que j'ajoute une propriété pour vérifier si on est sur un contenu de type article et ajouter la classe adéquate ↩︎