<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hugo | Julien Maxant</title><link>https://www.julien-maxant.com/tags/hugo/</link><description>Articles, projets et veille sur le thème « Hugo ».</description><generator>Hugo -- gohugo.io</generator><language>fr-FR</language><copyright>2026 - Julien Maxant</copyright><lastBuildDate>Sun, 12 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.julien-maxant.com/tags/hugo/index.xml" rel="self" type="application/rss+xml"/><item><title>Portfolio Hugo</title><link>https://www.julien-maxant.com/projets/portfolio-hugo/</link><guid>https://www.julien-maxant.com/projets/portfolio-hugo/</guid><pubDate>Sun, 12 Jul 2026 00:00:00 +0000</pubDate><description>Étude de cas : le site que vous consultez — pipeline CSS et qualité outillés pour tenir une baseline navigateurs et un contraste AAA sans y repasser à l'œil à chaque changement.</description><content:encoded><![CDATA[<p><strong>En bref :</strong> un site que je peux faire évoluer sans craindre de casser l&rsquo;existant.
Accessibilité, cohérence visuelle et qualité du build sont vérifiées automatiquement à
chaque modification, plutôt que relues à la main.</p>
<p>Techniquement : Hugo sans thème (layouts et partials écrits à la main), CSS sur mesure
sans framework, pipeline qualité (lint, tokens, contraste, build) qui tourne
à l&rsquo;identique en local et en CI.</p>
<h2 id="pourquoi-hugo">Pourquoi Hugo</h2>
<p>Projet perso avec un double objectif : une vitrine professionnelle pour recruteurs et
clients freelance, et un terrain d&rsquo;entraînement pour monter en compétence sur du
templating Go, du CSS sans dépendance, et une chaîne CI/CD tenue de bout en bout — sans
sacrifier la lisibilité du contenu à la démonstration technique.</p>
<p>Un générateur de site statique répond aux deux à la fois. Côté vitrine, la cible
(recruteurs, agents de recherche) n&rsquo;a pas de rendu JS à franchir pour indexer le
contenu, contrairement à une SPA. Côté apprentissage, le HTML/CSS produit reste la
sortie principale — pas de couche framework JS entre l&rsquo;auteur et le résultat, donc
chaque décision (tokens, contraste, breakpoints) reste visible et vérifiable dans la
feuille de style elle-même plutôt que dissoute dans un système de composants.</p>
<h2 id="contexte">Contexte</h2>
<ul>
<li>Site personnel, parti d&rsquo;un squelette : thème installé, contenu en Lorem Ipsum, une
seule page <code>content/_index.md</code></li>
<li>Aucune contrainte de delivery externe — le calendrier est le seul arbitre du scope</li>
<li>Exigence posée dès la Phase 0 (avant tout contenu) : les quality gates et la CI
existent avant que le contenu s&rsquo;accumule, pas après</li>
<li>Cible recruteurs/clients : le site doit rester une vitrine lisible, pas seulement un
prétexte à empiler des scripts de vérification</li>
</ul>
<h2 id="les-contraintes">Les contraintes</h2>
<ul>
<li>Pas de framework CSS, pas de PostCSS, pas d&rsquo;autoprefixer — seul <code>css.Build</code> (esbuild,
natif Hugo) prend en charge la transpilation et les préfixes vendeur</li>
<li>Une baseline navigateurs explicite (Chrome 105+, Firefox 121+, Safari 16+, Edge 105+)
à tenir, alors que deux mécanismes différents la couvrent : transpilation de syntaxe
d&rsquo;un côté, blocage de features runtime non transpilables de l&rsquo;autre</li>
<li>Contraste ciblé à AAA (7:1) plutôt que le AA/RGAA (4.5:1), sans dérive silencieuse
tolérée à mesure que la palette ou les composants évoluent</li>
<li>Aucune valeur de couleur, d&rsquo;espacement ou de breakpoint écrite en dur dans un
composant — tout doit venir d&rsquo;un token déclaré une seule fois</li>
<li>Le hook pre-commit local et la CI doivent exécuter la même définition, pour qu&rsquo;aucun
contrôle qualité ne puisse diverger entre les deux</li>
</ul>
<h2 id="les-choix-écartés">Les choix écartés</h2>
<ul>
<li>Une revue manuelle du contraste à chaque changement de palette : tolérable une fois,
pas répétable sans dérive — d&rsquo;autant que la palette est passée de AA à AAA en cours
de route, avec des marges initiales aussi fines qu&rsquo;entre 7,00 et 7,06</li>
<li>Une exemption CSS ajoutée « au cas où » dans la config Stylelint plutôt que prouvée :
une feature ignorée sans vérification empirique dans <code>public/</code> reste ignorée même
quand la baseline évolue et que le support natif la couvre déjà</li>
<li>Dupliquer la logique de vérification entre pre-commit et CI (deux configs qui
divergent tôt ou tard) plutôt qu&rsquo;une définition unique appelée par les deux</li>
</ul>
<h2 id="décisions-techniques">Décisions techniques</h2>
<ul>
<li><strong>Tokens CSS à deux niveaux d&rsquo;indirection</strong> : palettes brutes (<code>--light-*</code>,
<code>--dark-*</code>) jamais consommées directement par un composant, tokens sémantiques
(<code>--color-surface</code>, <code>--color-text-soft</code>…) seuls exposés. Le dark mode change en
réassignant les tokens sémantiques dans un seul fichier, sans toucher aux
composants.</li>
<li><strong>Règle « zéro valeur en dur » appliquée par script</strong>, pas seulement documentée :
<code>check-tokens.mjs</code> échoue sur toute couleur, taille ou durée littérale hors
<code>base/tokens.css</code> ; l&rsquo;échappatoire est un commentaire <code>token-exception</code> justifié
inline, jamais un ajout silencieux à une liste d&rsquo;ignore.</li>
<li><strong>Contraste vérifié automatiquement</strong> : <code>check-contrast.mjs</code> lit les valeurs hex
directement dans <code>tokens.css</code> (aucune valeur dupliquée dans le script) et calcule
chaque paire de couleurs, texte à 7:1, composants à 3:1. Un token de couleur non
couvert par une paire est aussi un échec — sinon un token ajouté plus tard n&rsquo;est
simplement jamais mesuré.</li>
<li><strong>Breakpoints déclaratifs mais vérifiés à l&rsquo;exécution</strong> : les media queries ne
peuvent pas lire une custom property, donc les valeurs (<code>768px</code>, <code>576px</code>) restent en
dur — mais <code>check-breakpoints.mjs</code> échoue sur une largeur qui ne correspond à aucun
token <code>--bp-*</code>, et sur un token que plus aucune query n&rsquo;utilise.</li>
<li><strong>Deux rôles distincts pour tenir la baseline navigateurs</strong> : <code>css.Build</code> transpile
la syntaxe (nesting, media query range syntax) à la compilation ; Stylelint
(<code>stylelint-no-unsupported-browser-features</code>) bloque au lint les features runtime
qu&rsquo;aucun transpileur ne peut simuler (container queries, <code>subgrid</code>). Chaque entrée de
la liste d&rsquo;ignore Stylelint est justifiée par une vérification dans <code>public/</code> après
build, pas supposée — deux entrées obsolètes (<code>:has()</code>, <code>scroll-behavior</code>) ont été
retirées une fois la baseline remontée et le support natif confirmé.</li>
<li><strong><code>lefthook.yml</code> comme unique source de vérité qualité</strong> : le hook pre-commit local
tourne sur les fichiers stagés, la CI appelle la même commande sur l&rsquo;ensemble des
fichiers trackés — aucune règle qualité ne peut exister dans l&rsquo;un sans exister dans
l&rsquo;autre.</li>
<li><strong>Contenu piloté par cascade Hugo plutôt que par template dédié</strong> : la section
<code>/veille/</code> (teaser-only) utilise <code>build.render = 'link'</code> en cascade pour rester dans
les collections (donc alimenter les pages <code>/tags/*</code>) sans générer de page de détail —
contre <code>render = 'never'</code>, qui exclurait l&rsquo;entrée de toute collection. Comportement
vérifié après build (<code>--cleanDestinationDir</code>) plutôt que supposé : aucune page
<code>public/veille/&lt;entrée&gt;/</code> générée, sitemap propre, RSS global exempt.</li>
</ul>
<h2 id="résultat">Résultat</h2>
<p>Pas de métrique de production comparable au cas Drupal — c&rsquo;est un site personnel, pas
un site à trafic. L&rsquo;angle est différent : une CI qui tolère zéro <code>WARN</code> Hugo, un
contraste et des tokens vérifiés par script plutôt que revus à l&rsquo;œil, et un pre-commit
qui ne peut pas diverger de la CI par construction. Le pipeline qualité tient à jour
avec le contenu, pas après coup.</p>
]]></content:encoded></item></channel></rss>