- CSS 76.5%
- HTML 23.5%
| assets/icons/lucide | ||
| exampleSite | ||
| layouts | ||
| static/css | ||
| LICENSE | ||
| README.md | ||
| theme.toml | ||
Ecomin
Thème Hugo éco-conçu minimal.
Principes
- Zéro JavaScript -- aucun script, jamais
- Zéro dépendance externe -- pas de CDN, webfont, analytics
- CSS unique -- une seule feuille de style, polices système, palettes de couleurs interchangeables
- Accessibilité -- WCAG 2.1 AA, HTML sémantique, navigation clavier
- Dark mode -- automatique via
prefers-color-scheme - Impression -- styles print intégrés
Installation
Ajouter le thème comme sous-module Git :
git submodule add <url-du-repo> themes/hugo-ecomin
Dans hugo.toml :
theme = "hugo-ecomin"
Site de démonstration
Le thème inclut un site d'exemple dans exampleSite/ :
cd exampleSite
hugo server
Configuration
Paramètres du site
[params]
author = "Nom" # meta author
subtitle = "Sous-titre" # affiché sous le titre sur la home
copyright = "CeCILL v2.1" # texte dans le footer
copyrightURL = "https://..." # lien optionnel sur le texte copyright
favicon = "/favicon.png" # chemin du favicon
paletteLight = "default-light" # palette mode clair (défaut)
paletteDark = "default-dark" # palette mode sombre (défaut)
Menu de navigation
La navigation principale utilise des icônes SVG colorées. Elle est alimentée par le menu main de Hugo et lit les paramètres icon et color depuis le frontmatter de chaque section cible.
[[menus.main]]
name = "Recettes"
url = "/recipes/"
weight = 10
[[menus.main]]
name = "Journal"
url = "/journal/"
weight = 20
Sections
Chaque section peut déclarer dans son _index.md :
| Paramètre | Type | Description |
|---|---|---|
title |
string | Titre de la section |
description |
string | Description affichée sous le titre |
icon |
string | Chemin de l'icône SVG (ex : lucide/package) |
color |
string | Couleur hexadécimale (bordures, fond, icône) |
sort |
string | Tri des pages enfants |
ressources |
list | Ressources associées (téléchargements, liens) |
related |
list | Chemins de pages liées (ex : /modules/module-1) |
Exemple :
---
title: Journal
description: Carnet de bord des réalisations.
icon: lucide/notebook-pen
color: "#4a6885"
sort: date-reverse
---
Tri des pages
Le paramètre sort dans le frontmatter d'une section contrôle l'ordre d'affichage de ses pages enfants :
| Valeur | Comportement |
|---|---|
| (absent) | Tri Hugo par défaut (weight, puis date) |
date |
Par date croissante |
date-reverse |
Par date décroissante |
title |
Alphabétique par titre |
weight |
Par poids |
Héritage icon/color
Les pages enfants qui n'ont pas d'icon ou de color héritent automatiquement de ceux de leur section parente. Cela fonctionne dans les listes (page-card) et les en-têtes (page-header).
Ressources
Fichiers téléchargeables affichés sous forme de badges dans le header :
ressources:
- label: "Support de cours"
target: /support.pdf
icon: lucide/file-text # optionnel, défaut : lucide/download
- label: "Exercices"
target: /exercices.pdf
color: "#6a4c93" # optionnel
| Champ | Type | Défaut | Description |
|---|---|---|---|
label |
string | -- | Texte du lien |
target |
string | -- | URL du fichier |
icon |
string | lucide/download |
Icône du badge |
color |
string | -- | Couleur personnalisée du badge |
Liens
Liens externes ou internes affichés en bas de page sous forme de page-cards :
links:
- label: "Documentation"
target: https://docs.example.com
description: "Guide complet du module"
- label: "Voir les exercices"
target: /exercices/
description: "Exercices associés"
| Champ | Type | Défaut | Description |
|---|---|---|---|
label |
string | -- | Titre du lien |
target |
string | -- | URL cible |
description |
string | -- | Description affichée sous le titre |
icon |
string | (auto) | lucide/external-link si https://, sinon lucide/arrow-right |
color |
string | -- | Couleur personnalisée |
Les liens https:// s'ouvrent automatiquement dans un nouvel onglet.
Pages liées
Pages liées affichées en bas de page sous forme de page-cards, précédées du titre "Voir aussi" :
related:
- /scenarios/scenario-1
- /modules/module-2
Les chemins invalides sont ignorés silencieusement.
Convention des partials
Les trois partials resources.html, links.html et related-pages.html suivent la même convention : ils reçoivent la page en contexte et gèrent eux-mêmes la condition d'affichage.
{{ partial "resources.html" . }}
{{ partial "links.html" . }}
{{ partial "related-pages.html" . }}
Templates
Structure des fichiers
layouts/
├── _default/
│ ├── baseof.html # Structure HTML : head, header/nav, breadcrumb, main, footer
│ ├── single.html # Page de contenu
│ ├── list.html # Liste de pages (avec tri configurable)
│ ├── taxonomy.html # Index de taxonomie / page de terme
│ └── terms.html # Liste des termes d'une taxonomie
├── partials/
│ ├── page-header.html # En-tête de page (panel coloré)
│ ├── page-header-body.html # Contenu commun du header (description, ressources, hook)
│ ├── page-card.html # Carte cliquable dans une liste
│ ├── page-cards.html # Liste de page-cards
│ ├── resources.html # Ressources téléchargeables (badges)
│ ├── links.html # Liens externes/internes (page-cards)
│ ├── hook.html # Dispatcher de hooks par section
│ ├── related-pages.html # Pages liées (bas de page)
│ ├── icon.html # Injection SVG inline
│ └── breadcrumb.html # Fil d'Ariane
├── index.html # Page d'accueil
├── shortcodes/
│ ├── callout.html # Bloc note/tip/warning/danger
│ ├── details.html # Accordéon HTML natif
│ ├── columns.html # Conteneur multi-colonnes
│ ├── column.html # Colonne individuelle
│ ├── badge.html # Badge inline coloré
│ ├── icon.html # Icône inline dans le texte
│ ├── mark.html # Surligneur coloré
│ ├── kbd.html # Raccourci clavier
│ ├── quote.html # Citation avec auteur/source
│ └── figure.html # Image avec légende et crédit
static/
└── css/
├── style.css # Feuille de style principale
└── palettes/ # Palettes de couleurs
├── default-light.css
├── default-dark.css
├── nord-light.css
├── catppuccin-latte.css
└── ...
baseof.html
Template de base avec blocks surchargeables :
| Block | Rôle |
|---|---|
head-extra |
Ajouter des balises dans <head> (feed Atom, meta, etc.) |
main |
Contenu principal (surchargé par chaque template) |
Sur la page d'accueil, le titre du site est rendu en <h1> dans le header. Sur les autres pages, c'est un lien <a> vers la home.
single.html
Utilise le partial page-header (panel coloré). Le contenu est rendu dans un <div class="post-content">.
list.html
Affiche un page-header suivi de la liste des pages enfants via le partial page-cards. Le tri est configurable via le paramètre sort du frontmatter.
Partials
page-header.html
Rendu de l'en-tête de page sous forme de panel (bordure gauche, fond teinté). Si la page a une color, le panel prend cette couleur. Sinon, il utilise --color-border (gris discret).
Délègue le contenu commun (description, ressources, hook meta) au partial page-header-body.html.
page-cards.html
Liste de page-cards. Deux variantes d'appel :
{{ partial "page-cards.html" (dict "pages" .Pages.ByTitle) }}
{{ partial "page-cards.html" (dict "terms" .Terms.Alphabetical) }}
resources.html
Liste de ressources (téléchargements, liens externes, liens internes). Reçoit une slice de ressources depuis le frontmatter :
{{ with .Params.ressources }}{{ partial "resources.html" . }}{{ end }}
page-card.html
Carte cliquable représentant une page dans une liste. Paramètres :
{{ partial "page-card.html" (dict "page" . "count" 42) }}
{{ partial "page-card.html" (dict "page" .) }}
| Paramètre | Type | Description |
|---|---|---|
page |
Page | Requis -- la page à afficher |
count |
int | Optionnel -- nombre affiché en badge |
Affiche : icône, titre, count, description. Hérite icon/color du parent.
Hooks
Deux zones extensibles dans chaque carte. Le mécanisme de résolution (hook.html) cherche un partial par section, puis un fallback générique :
| Zone | Partial par section | Fallback générique |
|---|---|---|
| Aside | page-card-aside-{section}.html |
page-card-aside-default.html |
| Footer | page-card-footer-{section}.html |
page-card-footer-default.html |
La résolution :
- Si
page-card-aside-recipes.htmlexiste, il est utilisé pour les pages de la sectionrecipes - Sinon, si
page-card-aside-default.htmlexiste, il est utilisé - Sinon, rien n'est rendu
Chaque hook reçoit la page en contexte.
Exemples d'override dans le site :
{{/* layouts/partials/page-card-aside-recipes.html */}}
{{/* Affiché à droite du titre, uniquement pour la section recipes */}}
{{ with .Params.rating }}<span class="rating">{{ . }}/5</span>{{ end }}
{{/* layouts/partials/page-card-footer-recipes.html */}}
{{/* Affiché sous la description, uniquement pour la section recipes */}}
{{ with .Params.allergens }}
<div class="tags">
{{ range . }}<span class="badge badge-allergen">{{ . }}</span>{{ end }}
</div>
{{ end }}
{{/* layouts/partials/page-card-footer-default.html */}}
{{/* Fallback pour toutes les sections sans partial spécifique */}}
{{ with .Params.tags }}
<div class="tags">
{{ range . }}<span class="badge">{{ . }}</span>{{ end }}
</div>
{{ end }}
breadcrumb.html
Fil d'Ariane automatique. Affiché sur toutes les pages sauf la home. Remonte jusqu'à 10 niveaux de profondeur.
icon.html
Charge un SVG inline depuis assets/icons/. Reçoit un chemin relatif :
{{ partial "icon.html" "lucide/house" }}
{{ partial "icon.html" "allergen/gluten" }}
Le chemin correspond à assets/icons/<chemin>.svg.
Les attributs aria-hidden="true" et focusable="false" sont ajoutés automatiquement à l'injection.
Icônes
Le thème embarque la bibliothèque Lucide complète (1700+ icônes) dans assets/icons/lucide/.
Les sites peuvent ajouter d'autres sets d'icônes en créant des sous-dossiers dans leur assets/icons/ (ex : assets/icons/allergen/, assets/icons/diet/).
CSS
Palettes de couleurs
Le thème propose des palettes de couleurs interchangeables. Chaque palette est un fichier CSS dans static/css/palettes/ qui définit les variables --color-* du thème.
Configuration dans hugo.toml :
[params]
paletteLight = "catppuccin-latte"
paletteDark = "catppuccin-mocha"
Sans configuration, le thème utilise default-light et default-dark. Un nom de palette invalide provoque une erreur de build explicite.
Palettes disponibles
| Palette | Clair | Sombre | Source |
|---|---|---|---|
| Default | default-light |
default-dark |
Palette originale du thème |
| Nord | nord-light |
nord-dark |
nordtheme.com |
| Gruvbox | gruvbox-light |
gruvbox-dark |
github.com/morhetz/gruvbox |
| Solarized | solarized-light |
solarized-dark |
ethanschoonover.com/solarized |
| Rosé Pine | rose-pine-dawn |
rose-pine-moon |
rosepinetheme.com |
| Catppuccin | catppuccin-latte |
catppuccin-frappe / catppuccin-macchiato / catppuccin-mocha |
catppuccin.com |
| Tango | tango-light |
tango-dark |
Tango Desktop Project |
| Adwaita | adwaita-light |
adwaita-dark |
GNOME HIG |
Les palettes paletteLight et paletteDark peuvent être mixées librement (ex : nord-light en clair + catppuccin-mocha en sombre). Pour forcer un seul mode, utiliser la même palette pour les deux.
Couleurs nommées
Chaque palette expose ses couleurs d'origine sous forme de variables CSS --palette-*. Les sites peuvent les utiliser dans leur CSS custom :
:root {
--color-accent: var(--palette-mauve); /* Catppuccin */
--color-accent: var(--palette-frost-1); /* Nord */
--color-accent: var(--palette-aqua); /* Gruvbox */
}
Créer une palette custom
Créer un fichier static/css/palettes/mon-theme.css :
:root {
--color-text: #...;
--color-bg: #...;
--color-link: #...;
--color-link-hover: #...;
--color-muted: #...;
--color-border: #...;
--color-accent: #...;
}
Puis dans hugo.toml :
[params]
paletteLight = "mon-theme"
Variables
Le thème utilise des variables CSS dans :root. Les couleurs sont fournies par la palette active. Les variables de mise en page et typographie sont dans style.css :
:root {
/* Typographie */
--font-body: Georgia, "Times New Roman", serif;
--font-ui: system-ui, -apple-system, "Segoe UI", sans-serif;
--font-mono: ui-monospace, "Courier New", monospace;
/* Mise en page */
--max-width: 88ch;
--spacing: 1.5rem;
--radius: 6px;
}
Variables dynamiques
Définies en inline par les templates :
| Variable | Source | Usage |
|---|---|---|
--level-color |
color du frontmatter |
Bordures, fond des cards et panels |
--nav-color |
color de la section cible |
Icônes de navigation |
--badge-color |
color de la ressource |
Badges de ressources |
Dark mode
Le mode sombre est géré par le système de palettes : paletteLight est chargée avec media="(prefers-color-scheme: light)" et paletteDark avec media="(prefers-color-scheme: dark)".
Impression
Les styles @media print masquent la navigation, le footer, le fil d'Ariane, les badges et les ressources. Le texte est noir sur fond blanc.
Animation
Toutes les transitions sont désactivées si l'utilisateur a activé prefers-reduced-motion: reduce.
Shortcodes
Composants réutilisables dans le contenu Markdown, zéro JavaScript.
callout
Bloc d'information coloré avec icône. 4 types disponibles :
{{</* callout type="note" */>}}
Ceci est une note informative.
{{</* /callout */>}}
{{</* callout type="tip" title="Conseil" */>}}
Contenu du conseil.
{{</* /callout */>}}
{{</* callout type="warning" */>}}
Message d'avertissement.
{{</* /callout */>}}
{{</* callout type="danger" title="Important" */>}}
Message critique.
{{</* /callout */>}}
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
type |
string | note |
Type : note, tip, warning, danger |
title |
string | (auto) | Titre personnalisé (défaut : Note/Astuce/Attention/Danger) |
details
Accordéon HTML natif (<details>/<summary>), sans JavaScript :
{{</* details summary="En savoir plus" */>}}
Contenu masqué par défaut.
{{</* /details */>}}
{{</* details summary="Ouvert par défaut" open="true" */>}}
Contenu visible au chargement.
{{</* /details */>}}
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
summary |
string | Détails |
Texte du bouton |
open |
string | -- | Si présent, le bloc est ouvert par défaut |
columns / column
Mise en page multi-colonnes, empilées automatiquement sur mobile :
{{</* columns */>}}
{{</* column */>}}
**Première colonne**
Contenu à gauche.
{{</* /column */>}}
{{</* column */>}}
**Deuxième colonne**
Contenu à droite.
{{</* /column */>}}
{{</* /columns */>}}
badge
Badge inline coloré :
Statut : {{</* badge */>}}stable{{</* /badge */>}} {{</* badge color="#b16286" */>}}v2.0{{</* /badge */>}}
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
color |
string | -- | Couleur personnalisée (--badge-color) |
icon
Icône SVG inline dans le texte :
Le thème utilise {{</* icon "lucide/heart" */>}} Lucide.
Le paramètre est le chemin de l'icône (identique au partial icon.html).
mark
Surligneur inline coloré :
Texte avec un {{</* mark */>}}passage surligné{{</* /mark */>}}.
En {{</* mark color="#94e2d5" */>}}couleur personnalisée{{</* /mark */>}}.
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
color |
string | #f9e2af |
Couleur de surlignage |
kbd
Raccourci clavier stylé. Les touches sont séparées par + :
Sauvegarder : {{</* kbd "Ctrl+S" */>}}
quote
Citation avec attribution :
{{</* quote author="Saint-Exupéry" title="Terre des hommes" */>}}
La perfection est atteinte, non pas lorsqu'il n'y a plus rien à ajouter,
mais lorsqu'il n'y a plus rien à retirer.
{{</* /quote */>}}
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
author |
string | -- | Nom de l'auteur |
title |
string | -- | Titre de l'œuvre (rendu en <cite>) |
source |
string | -- | URL de la source |
figure
Image avec légende et gestion des crédits. Le crédit est affiché en overlay discret en bas à droite de l'image.
{{</* figure src="photo.jpg" alt="Description" caption="Légende" */>}}
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
src |
string | -- | Nom du fichier (page resource) ou URL |
alt |
string | -- | Texte alternatif |
caption |
string | -- | Légende sous l'image |
credit |
string | -- | Crédit (override manuel) |
credit-url |
string | -- | Lien sur le crédit |
nocredit |
string | -- | Si présent, désactive l'exigence de crédit |
Résolution des crédits
Le shortcode impose un crédit pour chaque image. Le build échoue si aucune source de crédit n'est trouvée. La résolution suit cette chaîne de priorité :
- Paramètres du shortcode :
creditetcredit-url - EXIF/IPTC embarqué dans l'image (champs
Artist,Copyright,Credit,Byline) - Sidecar YAML : fichier
image.jpg.credit.yamlà côté de l'image - Fichier nocredit :
image.jpg.nocredit(crédit non requis) - Erreur de build si aucune source trouvée
Les étapes 2 à 4 ne s'appliquent qu'aux page resources (images dans le page bundle).
Sidecar .credit.yaml
Fichier YAML placé à côté de l'image dans le page bundle :
content/article/
├── index.md
├── photo.jpg
└── photo.jpg.credit.yaml
Format :
author: Photographe
url: https://source.com
license: CC BY 4.0
| Champ | Type | Description |
|---|---|---|
author |
string | Nom de l'auteur/photographe |
url |
string | Lien vers la source |
license |
string | Licence (affiché après l'auteur) |
Fichier .nocredit
Pour les images qui n'ont pas besoin de crédit (captures d'écran, images propres, domaine public), créer un fichier vide image.jpg.nocredit :
content/article/
├── index.md
├── screenshot.png
└── screenshot.png.nocredit
Pour les URLs externes, utiliser le paramètre nocredit=true du shortcode.
Accessibilité
- Lien d'évitement
<a href="#main">en premier élément du body <h1>unique par page (dans le header sur la home, dans le contenu ailleurs)- Fil d'Ariane avec
<nav aria-label="Fil d'Ariane"> - Navigation avec
aria-label,aria-current="page",titlesur les icônes - Focus visible :
outlineexplicite sur:focus-visible - SVG décoratifs :
aria-hidden="true" focusable="false"injectés automatiquement - Polices en
rem, base 16px minimum - Contraste minimum 4.5:1 (texte normal), 3:1 (grands textes)
- Largeur de lecture limitée à
--max-width
Licence
CeCILL v2.1 -- licence libre compatible GPL, de droit français.