Background image: Matthieu's World Background image: Matthieu's World
Social Icons

Comprendre le fonctionnement d’un thème Ghost

8 min de lecture
Par Matthieu Cousin

Table des matières

Les thèmes Ghosts, c'est cool, on peut faire beaucoup de modifications avec le code injection. En revanche, si vous voulez vraiment paramétrer votre thème pour qu'il suive certaines règles automatiquement et donc que vous n'ayez pas à rajouter du code injection en script ou en CSS pour chaque cas de figure, il va falloir mettre les mains dans le cambouis et toucher aux fichiers du thème.

Je vais essayer de vous détailler les différents fichiers que j'ai modifiés et qui sont (selon moi) les plus importants dans un thème Ghost.

Il faut savoir qu'un thème Ghost est écrit en HBS, aussi appelé "Handlebars", un langage que je n'avais jamais rencontré auparavant.

Il est organisé comme suit :

📂 Rôle des principaux fichiers dans un thème Ghost

Élément / Fichier Portée / Type Exemple de fichier Que contient-il ?
Template principal Page d’accueil index.hbs Point d’entrée principal du site, appelle les blocks comme grid-posts, classic-posts, intro texte, etc.
Template de page Pages CMS (ex. à propos, contact) page.hbs Rendu d’une page unique écrite dans Ghost, avec {{content}}, titre, image principale.
Template de tag (défaut) Liste les articles qui ont un certain tag (x) tag.hbs Rendu d’une page /tag/x/, avec header personnalisé (le nom du tag et une description) et la liste d’articles en dessous.
Template de tag (personnalisé) Liste des articles d’un tag (x) avec un affichage particulier tag-x.hbs. Rendu d’une page /tag/x/ avec un affichage personnalisé. Court-circuite grid-post
Listing / Grille Liste d’articles (home, tag...) home-blocks/grid-posts.hbs Boucle sur plusieurs posts, appelle article-router.hbs, gère Masonry, nombre de colonnes.
Router de post Choix de la tuile à utiliser loop/article-router.hbs En fonction des tags (arc, quete, etc.), choisit le bon layout d’article : grid-article, arc-article, quete-article.
Tuile d’article standard Carte cliquable d’un article loop/grid-article.hbs Markup HTML d’un seul post (image, titre, extrait...), affiché dans les grilles. Aucun layout global, que la tuile.
Tuile alternative Pour arcs / quêtes / designs personnalisés loop/arc-article.hbs, loop/quete-article.hbs Variante de grid-article, spécialement stylée pour les arcs ou quêtes.
Template d’article complet Vue détaillée d’un post default-post-light.hbs (template de base) custom-template-perso.hbs (personnalisation) Un template de base permet de choisir ce qu'on veut dans tous nos articles. Un template un peu plus personnalisé permet de faire du cas par cas selon les articles. C'est ce template qu'on peut sélectionner lors de la création du post.
Fichier de routing Logique globale de routage Ghost routes.yaml Définit quels fichiers hbs sont utilisés pour quelles URL (channels, collections...). Filtrage des posts possible par tag, auteur, etc.

🧩 Et le fichier default.hbs ?

C’est le layout global du thème. Tous les fichiers .hbs principaux (index.hbs, tag.hbs, etc.) héritent implicitement de lui grâce à la directive {{!< default}}.

Il contient :

  • le <head> avec les metas et le CSS,
  • les injections Ghost ({{ghost_head}}, {{ghost_foot}}),
  • le <body> avec les classes utiles (home-template, tag-template, etc.),
  • un conteneur pour le contenu : {{{body}}}.

Il ne décide d’aucun contenu, mais il enveloppe tous les autres. C’est donc le bon endroit pour :

  • les scripts JS globaux,
  • les hooks CSS communs,
  • le layout de base (header/footer/nav).

🧭 Comment ça s'imbrique ?

À chaque étape du processus, c’est le routes.yaml qui est consulté en premier. routes.yaml va donner des directives.
Ce fichier est présent en dehors du thème, il est accessible dans le setting de ghost dans le lab (ghost v5)
S’il est vide alors il y a 4 cas de figure principaux à connaître que Ghost va suivre pour choisir ce qu'il affiche sur la page.

1. Page d’accueil (index)

index.hbs
 └─ home-blocks/grid-posts.hbs
     └─ loop/article-router.hbs
         ├─ loop/grid-article.hbs   (par défaut)
         ├─ loop/quete-article.hbs  (si tag quete/now)
         └─ loop/arc-article.hbs    (si tag arc)
  • L’URL est la racine (pas une page particulière). On est donc sur la page d’accueil. Cela déclenche la page index.hbs qui appelle le fichier qui gère l’agencement des posts entre eux (grid-posts).
  • grid-posts dit alors (dans mon cas) « affiche les posts au format Masonry (2 colonnes, 6 articles maximum par page, pagination) » et appelle article-router.
  • article-router détermine l’affichage de la tuile en fonction de certaines caractéristiques (par exemple, la présence d’un tag).

2. Page particulière

Si on arrive sur une URL de type site.com/page, alors c’est page.hbs qui est utilisé. Pas besoin d’entrer dans les détails ici, Ghost gère très bien cette partie.

3. Page de tag particulier

Dans Ghost, chaque tag peut avoir sa propre page (/tag/nomdutag), listant tous les articles qui le contiennent. C’est extrêmement pratique pour organiser ses contenus.

Mais parfois, on veut un affichage particulier pour certains tags. Exemple :

Dans ce cas :

  • Ghost va chercher un fichier tag-<slug>.hbs (ex. tag-quete.hbs pour /tag/quete/).
  • Si ce fichier existe, il est utilisé à la place de tag.hbs.
⚠️ C’est une solution simple mais peu robuste. Ghost choisit tag-quete.hbs uniquement par convention de nommage, pas parce qu’on lui a demandé explicitement. En cas de mise à jour du core de Ghost, cela pourrait sauter.
Il vaut mieux définir cela dans routes.yaml.

Si on utilise un fichier tag-quete.hbs :

  • Il fixe son propre agencement (sans passer par grid-postssi on ne lui demande pas).
  • Il appelle directement un type de tuile (ex : quete-article.hbs), sans passer par article-router.

Illustration sur mon blog personnel :

  • /tag/arc/tag-arc.hbsarc-article.hbs
  • /tag/quete/tag-quete.hbsquete-article.hbs
  • /tag/now/tag-now.hbsquete-article.hbs

4. Page de tag sans règle particulière

Si aucun fichier spécial n’est trouvé pour l'url demandée, qu'on est pas sur la page d'acceuil et qu'on est ur une page de tag, Ghost utilise tag.hbs.

  • tag.hbs affiche le nom et la description du tag.
  • Il appelle grid-posts.hbs pour gérer l'agencement des tuiles de post entre elles
  • grid-posts appelle article-router.hbs pour gérer l'apparence de chaque tuile
  • article-router choisit le bon template de tuile selon les tags (ex : grid-article.hbs, arc-article.hbs, etc.).
Navigateur → URL → routes.yaml (si non vide) → index.hbs / tag.hbs
                                      ↓
                                 grid-posts.hbs
                                      ↓
                             article-router.hbs
                             ↙       ↓       ↘
                 grid-article   quete-article   arc-article

🛣️ Alternative : routes.yaml en pilote principal

Avec routes.yaml, on peut tout définir plus proprement les fichiers que doit utiliser ghost selon l'url. Exemple de mon fichier route.yaml

routes:
  /tag/arc/:
    controller: channel
    template: tag-arc
    filter: 'tag:arc'
    data: tag.arc             # slug explicite → description OK

  /tag/quete/:
    controller: channel
    template: tag-quete
    filter: 'tag:quete'
    data: tag.quete

  /tag/now/:
    controller: channel
    template: tag-now
    filter: 'tag:now+tag:quete'
    data: tag.now

collections:
  /:                           # Accueil
    permalink: /{slug}/
    template: index
    filter: 'primary_tag:-[arc,quete]'   # masque arc/quete seulement ici
    order: 'published_at desc'

taxonomies:                    # ← laisse Ghost servir TOUS les autres /tag/{slug}/
  tag: /tag/{slug}/
  author: /author/{slug}/
URL Template appelé Source du mapping {{tag}} dispo ? Remarques utiles
/ index.hbs collections dans routes.yaml Accueil ; liste tous les articles selon le filtre de collection
/tag/arc/ tag-arc.hbs routes explicite data.tag.arc Page spéciale pour les arcs (timeline, etc.)
/tag/quete/ tag-quete.hbs routes explicite data.tag.quete Page spéciale pour les quêtes
/tag/now/ tag-now.hbs routes explicite data.tag.now Page spéciale pour les quêtes en cours
/tag/{slug}/ (ex. /tag/voyage/) tag.hbs taxonomies dans routes.yaml ✅ automatiquement Rendu dynamique pour les autres tags

Je ne maîtrise malheureusement pas très bien ce fichier route.yaml. Je suis preneur si vous avez des suggestions.

La seule limite que je donnerais à route.yaml, c'est que comme on ne peut pas mettre de JavaScript et qu'on ne peut pas utiliser un langage très complexe, il est difficile de faire un filtrage fin de certains éléments, par exemple filtrer des primary tags de manière fine (sur un nom excate et pas seulement un "include")

Le route.yaml est excellent pour mettre en place des règles d'affichage simples sur son site. Dès que les règles sont complexe, il vaut mieux passer par des appels de fichier hbs directement das le thème


🛠️ Points clés pour la personnalisation

Action À modifier
Masquer les articles avec les tags arc ou quete Dans grid-posts.hbs (filtrage Handlebars ou JS). En pratique, je n'ai pas réussi.
Styliser différement les tuiles par défaut Agir sur grid-article.hbs
Modifier la structure d’une page de tag Dans tag.hbs ou tag-*.hbs.
Changer l’agencement des tuiles sur une page Dans grid-posts.hbs.
Ajouter un nouveau type de tuile Créer loop/mon-type-article.hbs, puis l’intégrer à article-router.hbs.
Ajouter un badge ou ruban à une tuile Ajouter des classe css dans grid-article.hbs, avec des conditions d’affichage.
Styliser avec des hooks CSS Utiliser les classes ajoutées dans les .hbs (.grid-33, .quete-card, etc.).

❌ Les limites que j’ai rencontrées

J’ai réussi à personnaliser mon thème comme je le voulais sur certaines pages de tags spécifiques, en particulier :

J’ai aussi pu créer des pages de tags comme :

Mais j’aurais aimé empêcher l’affichage de certains types d’articles ailleurs que sur leur page dédiée. Notamment les articles avec un primary_tag comme "quête" ou "arc", qui ont un affichage non compatible avec Masonry. Ils déforment visuellement la page d’accueil ou les autres tags.

Après de nombreux essais infructueux, voici ce que j’ai observé :

  • Les filtres sur primary_tag sont difficiles à implémenter avec du handle bar, surtout si il faut faire la différence entre un tag exacte (exemple : arc) et un tag qui contient arc (exemple arc-1)
  • La pagination se casse lorsqu’on essaie de filtrer avec {{#get}}.
  • Le Masonry devient instable avec certains layouts.

Sur ce point, je suis preneur de solutions ou de conseils.


🚧 Pistes d'amélioration

  • Centraliser la logique dans routes.yaml pour éviter les tag-x.hbs redondants.
  • Ne pas court-circuiter grid-post.hbs avec tag-x.hbs : créer un équivalent propre pour les pages (ex. un grid-router.hbs).
  • Ajouter des règles JS pour mieux gérer les exclusions dynamiques.

🧩 Conclusion

Modifier un thème Ghost en profondeur peut sembler intimidant au départ, surtout quand on découvre Handlebars pour la première fois. Mais une fois la logique comprise — entre les fichiers de structure, les templates de tuiles, le routes.yaml, on se rend compte qu’on peut vraiment créer une expérience sur mesure. N'hésitez pas à demander de l'aide à ChatGPT mais attention il peut vous faire tourner en boucle un moment !

Ce guide n’a pas vocation à être exhaustif, mais à partager ce que j’ai appris en pratiquant, avec mes réussites et mes limites. Si ça peut éviter à d'autres de perdre des heures à débuguer une page d’accueil cassée ou un tag qui n’affiche pas ce qu’il faut, c’est déjà une victoire.

Et si vous avez trouvé d’autres astuces, des contournements élégants ou des bonnes pratiques à partager, je suis preneur !