Juicer
Français

Fonctionnalités de blogue

Mots-clés, catégories, pagination, dates, temps de lecture — l’ensemble de fonctionnalités qui transforment un site juicer en blogue.

Les fonctionnalités de site de documentation de juicer (sections, _index.md, partials, thèmes) conviennent telles quelles à un blogue, mais un vrai blogue veut habituellement quatre choses de plus : un moyen d’étiqueter les articles, des pages d’archive qui regroupent les articles par mot-clé, des pages de liste paginées pour que la page d’accueil ne traîne pas, et des dates de publication analysées pour que les pages de liste puissent trier par récence. Cette page couvre tout cela, plus les estimations de temps de lecture et le thème juicerblog fourni qui exerce chaque fonctionnalité.

Note

Chaque fonctionnalité de cette page est optionnelle. Un site de documentation qui ne définit pas de frontmatter tags, ne configure pas paginate et ne fournit pas de frontmatter date se rendra exactement de la même manière après ces fonctionnalités qu’avant — mêmes fichiers, mêmes URL, sortie identique à l’octet près.

Mots-clés et catégories

Ajoutez un champ tags au frontmatter d’un article pour le marquer. Une liste ou une simple chaîne sont toutes deux acceptées :

---
title: Whirlwind tour of Scala 3 enums
date: 2024-03-12
tags: [scala, language]
---
---
title: A short note
tags: meta
---

Deux pages d’archive sont émises pour chaque mot-clé utilisé par le site :

Motif d’URLCe qu’elle liste
/tags/Chaque mot-clé utilisé par le site, avec le nombre d’articles
/tags/<slug>/Chaque article étiqueté avec ce mot-clé précis

Les noms de mots-clés sont « slugifiés » pour les URL — mis en minuscules, repliés en ASCII (cafécafe), et toute suite de caractères non alphanumériques est réduite à un seul -. Ainsi un mot-clé nommé "Functional Programming" devient /tags/functional-programming/.

categories est analysé à l’identique et traité comme un axe distinct. Vous pouvez utiliser les deux ou aucun — les sites qui veulent une seule façon de regrouper les articles en choisissent un et ignorent l’autre :

---
title: Setting up the studio
date: 2024-03-12
categories: [behind-the-scenes]
tags: [meta, design]
---

Cet article serait atteignable à /categories/behind-the-scenes/ ainsi qu’à /tags/meta/ et /tags/design/.

Gabarits

Les archives de mots-clés et de catégories ont besoin de leurs propres layouts. Le thème juicerblog livre une paire par défaut ; si vous montez votre propre thème, déposez ces deux layouts sous layouts/_default/ :

Fichier layoutCe qu’il rend
tag-list.htmlL’index /tags/ — répertoire complet de chaque mot-clé
tag-page.htmlL’archive d’un seul mot-clé, p. ex. /tags/scala/

Les mêmes noms avec category-list.html / category-page.html couvrent l’axe des catégories.

Les données que voient vos layouts de mots-clés :

{{ .terms }}                 // List of every term — for tag-list.html
{{ .terms[0].name }}         // "scala"
{{ .terms[0].slug }}         // "scala"
{{ .terms[0].url }}          // "/tags/scala/"
{{ .terms[0].count }}        // 7

{{ .term }}                  // The current term — for tag-page.html
{{ .term.name }}             // "scala"
{{ .term.pages }}            // List of pages tagged with this term
{{ .term.pages[0].title }}   // "Whirlwind tour of Scala 3 enums"
Tip

Si un layout est absent, l’archive correspondante est silencieusement sautée. Ainsi un site de documentation qui ne livre ni tag-list.html ni tag-page.html n’obtiendra pas d’URL de mots-clés même si vous écrivez accidentellement tags: [foo] quelque part.

Accès depuis n’importe quel gabarit

Les gabarits qui ne sont pas des archives de mots-clés — disons, votre topbar.html — peuvent quand même itérer sur chaque mot-clé utilisé par le site :

{{ .site.tags }}            // List of every tag, sorted by count desc
{{ .site.categories }}      // Same shape, for categories

Chaque entrée porte name, slug, url, count, pages — même forme que .terms.

Pagination

Un blogue avec trente articles ne devrait pas rendre chaque article sur la page d’accueil. Définissez paginate dans site.toml pour découper les pages d’index de section en plusieurs pages :

paginate = 10
sortBy = "date"

Cela indique à juicer :

Découper les pages de section

Pour chaque page d’index de section (chaque _index.md), prendre les pages enfants de la section et les diviser en tranches de 10.

Rendre la tranche 1 vers index.html

La première tranche atterrit à l’URL naturelle de la section — /posts/index.html, /index.html pour la racine du site, etc.

Rendre les tranches 2..N vers page/N/index.html

La deuxième tranche atterrit à /posts/page/2/index.html, la troisième à /posts/page/3/, et ainsi de suite. Compatible avec l’hébergement statique : chaque URL est un vrai répertoire avec un vrai fichier.

Si une section a moins de pages que paginate, seule la première tranche est émise et total vaut 1 — votre gabarit n’a pas besoin de traiter ce cas spécialement.

sortBy

ValeurOrdre
"date".page.date décroissant (plus récent d’abord) ; le défaut du blogue
"title".page.title croissant (alphabétique)
"weight"frontmatter weight croissant — comme le défaut de juicer

Une page avec un frontmatter weight supplante quel que soit le tri utilisé par la section, vous pouvez donc épingler un article « Bienvenue » au-dessus des listings triés par date.

Surcharges par section

Définissez paginate ou sortBy dans le frontmatter du _index.md d’une section pour supplanter le réglage du site :

---
title: Notes
paginate: 20
sortBy: title
---

A handful of short, alphabetised notes.

Gabarits

Les données que voient les layouts — tant les layouts _index que les archives de mots-clés :

{{ .section.paginator.current }}     // 1-based index of THIS slice
{{ .section.paginator.total }}       // total slice count
{{ .section.paginator.pages }}       // pages on THIS slice (already sliced)
{{ .section.paginator.first }}       // URL of slice 1
{{ .section.paginator.last }}        // URL of last slice
{{ .section.paginator.prevURL }}     // empty string on slice 1
{{ .section.paginator.nextURL }}     // empty string on the last slice

Un pied de pagination typique :

{{ if .section.paginator.prevURL }}
  <a href="{{ .section.paginator.prevURL }}">← Newer</a>
{{ end }}
<span>Page {{ .section.paginator.current }} of {{ .section.paginator.total }}</span>
{{ if .section.paginator.nextURL }}
  <a href="{{ .section.paginator.nextURL }}">Older →</a>
{{ end }}

Dates

Le frontmatter date est analysé en un véritable horodatage, pas une chaîne transmise telle quelle. Trois formes d’entrée sont reconnues :

FormeTraitée comme
2024-03-12T10:30:00ZISO-8601 complet avec décalage
2024-03-12T10:30:00Date-heure locale — supposée UTC
2024-03-12Date simple — minuit UTC

Si date est absente, juicer retombe sur la date de modification (mtime) du fichier markdown source. Cela signifie qu’un article fraîchement écrit se trie correctement sans que vous ayez à définir date explicitement.

Rendu

Trois assistants pré-formatés accompagnent la valeur analysée, pour que les gabarits n’aient pas à appeler de fonctions de formatage :

ChampExempleÀ utiliser pour
.page.dateOffsetDateTimeTri, calculs
.page.dateISO2024-03-12T00:00:00ZAttributs <time datetime=...>
.page.dateLongMarch 12, 2024Corps de texte
.page.dateShort2024-03-12Pages de liste compactes

Une ligne de méta-article standard :

<time datetime="{{ .page.dateISO }}">{{ .page.dateLong }}</time>
Note

Les noms de mois anglais en toutes lettres (January, February, …) sont codés à la main dans juicer plutôt que pilotés par un DateTimeFormatter conscient de la locale. La raison est purement pratique : la construction Native de juicer livre une base de données de locales minimale et MMMM y retombe sur M01/M02. Les noms de mois codés à la main se rendent à l’identique sur les cibles JVM, JS et Native. Si vous avez besoin d’un format long non anglais dès maintenant, écrivez votre propre assistant à partir de .page.dateShort plus votre propre table de mois.

Temps de lecture et nombre de mots

Deux champs de plus calculés automatiquement pour chaque page :

ChampTypeQuoi
.page.wordCountintNombre de mots du corps HTML rendu (après les shortcodes, avant le retrait)
.page.readingTimeintMinutes — ceil(wordCount / 200), avec un plancher de 1 pour les pages non vides

Le chiffre de 200 mots par minute est la moyenne popularisée par Medium. Si vous voulez une autre cadence, rendez wordCount directement et divisez-le vous-même dans le gabarit :

{{ .page.readingTime }} min read
about {{ .page.wordCount }} words

Les pages vides obtiennent wordCount = 0 et readingTime = 0 pour qu’un _index.md embryonnaire ne dise pas « 1 min de lecture » de façon trompeuse.

Permaliens

Par défaut, l’URL que juicer émet pour une page est en correspondance un pour un avec son emplacement dans l’arborescence content/ : content/posts/hello.md devient /posts/hello/. C’est le bon comportement pour les sites de documentation où la disposition des fichiers est l’architecture de l’information. Pour un blogue, vous voulez habituellement autre chose — la plupart des blogues routent chaque article par /<année>/<mois>/<slug>/, que l’article réside dans content/posts/, content/posts/scala/ ou content/drafts/.

Configurez cela avec la table [permalinks] dans site.toml :

[permalinks]
posts = ":year/:month/:slug/"
notes = ":slug/"

Chaque clé est le nom de section (le premier segment de chemin après le retrait de htmlDir). Chaque valeur est un gabarit d’URL avec des jetons de substitution. Un article sous content/posts/2024-03-12-hello.md avec date: 2024-03-12 dans son frontmatter se rend en :

URL par défautAvec posts = ":year/:month/:slug/"
/posts/2024-03-12-hello//2024/03/2024-03-12-hello/

(Astuce : quand vous utilisez des permaliens préfixés par date, retirez le préfixe de date du nom de fichier et laissez stripPrefix faire son travail — gardez l’URL propre.)

Les jetons disponibles

JetonSe résout en
:slugLe nom de fichier nettoyé (préfixes numériques retirés, suites non alphanumériques réduites)
:titleslugify(.page.title) — le title du frontmatter mis en minuscules et replié en ASCII
:yearAnnée à 4 chiffres depuis .page.date
:monthMois à 2 chiffres depuis .page.date
:dayJour à 2 chiffres depuis .page.date
:sectionLe nom de section lui-même — utile dans des motifs imbriqués comme :year/:section/:slug/

:title et :slug sont habituellement différents — le slug vient du nom de fichier (donc 01-getting-started.md devient getting-started), le titre vient du frontmatter title: (donc "Getting Started" devient getting-started aussi, mais "Café au Lait" devient cafe-au-lait). Utilisez :title quand les noms de fichiers sont opaques ou numérotés et que vous voulez quand même des URL lisibles :

[permalinks]
posts = ":year/:title/"

Les pages d’index de section restent en place

Les fichiers _index.md ne sont jamais routés par un gabarit de permalien. Même quand posts = ":year/:slug/" est défini, la page posts/_index.md réside toujours à /posts/, pas à /2024/posts/. La raison est structurelle : le _index.md décrit une section, pas un morceau de contenu, et l’URL d’une section doit être prévisible pour que les liens de votre navigation et de votre fil d’Ariane continuent de fonctionner.

Les sections que vous ne listez pas gardent leur URL par défaut

[permalinks] est une carte de surcharges, pas un interrupteur tout-ou-rien. Les sections qui ne sont pas des clés dans la table se rendent à leur URL de chemin physique par défaut. Ainsi un site avec à la fois des articles de blogue (sous /posts/ permalié) et des pages de prose (about.md, contact.md à la racine) liste simplement posts dans [permalinks] et laisse le reste tranquille :

[permalinks]
posts = ":year/:month/:slug/"
# `about.md` is still at /about/, `contact.md` still at /contact/.# `about.md` is still at /about/, `contact.md` still at /contact/.
Tip

Si un gabarit de permalien utilise :year / :month / :day pour un article qui n’a pas de frontmatter date:, juicer retombe sur la date de modification (mtime) du fichier markdown source. Cela signifie qu’un article fraîchement écrit obtient la date courante même quand vous avez oublié de définir le frontmatter — pratique pour les brouillons, mais bon à savoir quand vous faites git mv sur des fichiers et trouvez l’URL déplacée.

Ce qui se passe sur le disque

Les gabarits de permalien changent à la fois l’URL et l’emplacement d’écriture sur le disque : juicer ne garde pas deux copies de la page. Ainsi avec posts = ":year/:month/:slug/", un article s’écrit uniquement vers <dst>/2024/03/hello/index.html ; le répertoire hérité <dst>/posts/hello/ n’est jamais créé. (Si vous définissez htmlDir = "html" dans site.toml, le chemin est <dst>/html/2024/03/hello/index.html, puisque htmlDir est le préfixe de système de fichiers retiré des URL.)

Archives par date

La plupart des blogues finissent par vouloir une navigation « parcourir les articles par date » : une page d’archive pour chaque année, éventuellement avec une sous-archive pour chaque mois. Activez-la avec une ligne dans site.toml :

dateArchives = true

Juicer émet alors deux sortes de pages, conditionnées indépendamment par la présence de leurs layouts :

LayoutMotif d’URLCe qu’il liste
_default/date-year.html/<année>/index.htmlChaque article daté de cette année, plus un récapitulatif par mois
_default/date-month.html/<année>/<mois>/index.htmlChaque article daté de cette année + ce mois

Les deux layouts sont optionnels. Livrez le layout d’année pour obtenir des archives à l’année seulement ; livrez les deux pour une granularité au mois complète ; n’en livrez aucun et dateArchives = true ne fait rien de visible.

Note

Seules les pages avec un frontmatter date: explicite entrent dans les archives. Les pages dont le .page.date provient du repli sur la mtime du système de fichiers sont exclues — sinon un site de documentation qui active la fonctionnalité trouverait soudain chaque page de référence dans l’archive de l’année courante. La règle est « si vous avez défini une date, vous y êtes ; sinon, vous n’y êtes pas ».

Données d’archive d’année

Le layout date-year.html voit :

{{ .year }}                // 2024 (BigDecimal)
{{ .pages }}               // every dated post in 2024, newest first
{{ .pages[0].title }}      // most recent post's title
{{ .months }}              // per-month roll-up, ascending by month number
{{ .months[0].month }}     // 1 (BigDecimal)
{{ .months[0].monthName }} // "January"
{{ .months[0].url }}       // "/2024/01/"
{{ .months[0].count }}     // 4 (BigDecimal)
{{ .months[0].pages }}     // pages in that month, newest first

Un layout d’archive d’année typique :

<h1>{{ .year }}</h1>
<ol>
  {{ for m <- .months }}
  <li>
    <a href="{{ m.url }}">{{ m.monthName }}</a>
    <span>({{ m.count }} posts)</span>
  </li>
  {{ end }}
</ol>

Données d’archive de mois

Le layout date-month.html voit :

{{ .year }}        // 2024
{{ .month }}       // 3 (BigDecimal)
{{ .monthName }}   // "March"
{{ .pages }}       // posts in March 2024, newest first
<h1>{{ .monthName }} {{ .year }}</h1>
<ol>
  {{ for p <- .pages }}
  <li>
    <time datetime="{{ p.dateISO }}">{{ p.dateShort }}</time>
    <a href="{{ p.url }}">{{ p.title }}</a>
  </li>
  {{ end }}
</ol>

Combiner avec les permaliens

Les archives par date se composent proprement avec les gabarits de permalien : l’espace d’URL reste hiérarchique et unique. Avec :

dateArchives = true

[permalinks]
posts = ":year/:month/:slug/"

un seul article avec date: 2024-03-15 réside à /2024/03/hello/, et les pages d’archive couvrent ce même préfixe :

URLQuoi
/2024/03/hello/L’article unique
/2024/03/Archive de mars 2024
/2024/Archive de l’année 2024

Il n’y a pas de collision parce que les chemins de sortie des permaliens et les chemins d’archive atterrissent à des endroits différents — un article permalié s’écrit vers /<année>/<mois>/<slug>/index.html, tandis que l’archive d’année s’écrit vers /<année>/index.html et l’archive de mois vers /<année>/<mois>/index.html. La structure de répertoires s’entrelace proprement.

Séries / articles en plusieurs parties

Certains articles se lisent mieux en série — trois parties sur le débogage, quatre sur la mise en place d’un projet, douze sur l’écriture de votre propre OS. Juicer relie les articles apparentés en une série navigable via deux champs de frontmatter :

---
title: OS Internals, Part 1 — The Boot Process
series: OS Internals
seriesOrder: 1
---
---
title: OS Internals, Part 2 — The Memory Subsystem
series: OS Internals
seriesOrder: 2
---

Les pages partageant la même valeur series (sensible à la casse, correspondance exacte) sont reliées. Chacune voit un bloc .page.series :

ChampQuoi
.page.series.nameLe nom de série tel qu’il apparaît dans le frontmatter
.page.series.pagesListe de chaque page de la série, ordonnée
.page.series.prevL’enregistrement de la page précédente, ou null sur la première
.page.series.nextL’enregistrement de la page suivante, ou null sur la dernière
.page.series.indexPosition de la page courante (à partir de 1)
.page.series.totalNombre de pages dans la série

Une barre latérale « dans cette série » typique :

{{ if .page.series }}
<aside aria-label="In this series">
  <h2>{{ .page.series.name }}</h2>
  <p>Part {{ .page.series.index }} of {{ .page.series.total }}.</p>
  <ol>
    {{ for s <- .page.series.pages }}
    <li><a href="{{ s.url }}">{{ s.title }}</a></li>
    {{ end }}
  </ol>
</aside>
{{ end }}

Le thème juicerblog livre exactement ce widget sous forme du partial partials/series-nav.html ; _default/file.html l’appelle après le corps de l’article. Supplantez-le depuis le partials/ de votre site si vous voulez un texte personnalisé.

Règles d’ordonnancement

Au sein d’une série, les pages se trient par :

seriesOrder croissant

Les pages avec un seriesOrder explicite viennent en premier dans l’ordre qu’elles déclarent. Une page avec seriesOrder: 2 se trie avant une avec seriesOrder: 5.

Date croissante (plus ancien d’abord)

Les pages SANS seriesOrder se trient par .page.date croissant. Les séries se lisent habituellement chronologiquement — les articles plus anciens avant les plus récents — et le sens croissant correspond à cela.

Nom de fichier, comme départage stable

Deux articles non ordonnés du même jour retombent sur l’ordre du nom de fichier, pour que le résultat soit déterministe quel que soit l’ordre de parcours du système de fichiers.

Note

Une page peut être dans au plus une série — series: est une chaîne, pas une liste. Si vous avez besoin d’une relation d’appartenance multiple, c’est exactement à ça que sert tags. Les séries sont pour les articles liés narrativement qui partagent un seul arc.

Registre d’auteurs

Un blogue à auteur unique peut s’appuyer sur .site.author depuis site.toml et s’en contenter. Un site multi-auteurs a besoin de plus : biographie, avatar, liens externes par auteur, et une page d’archive qui liste les articles de chaque auteur.

Déclarez les auteurs comme un tableau de tables :

[[authors]]
id     = "ed"
name   = "Edward A Maxedon"
email  = "ed@example.com"
bio    = "Writes a lot of code."
avatar = "/img/ed.jpg"

[[authors.links]]
label = "GitHub"
url   = "https://github.com/edadma"

[[authors.links]]
label = "Mastodon"
url   = "https://hachyderm.io/@edadma"

[[authors]]
id     = "alice"
name   = "Alice Smith"
bio    = "Does a lot of design."
avatar = "/img/alice.jpg"

Puis pointez un article vers un (ou plusieurs) des id enregistrés :

---
title: A solo post
author: ed
---
---
title: A co-authored post
authors: [ed, alice]
---

author: est le raccourci singulier ; authors: est la forme multi-auteurs. L’une ou l’autre fonctionne. Les gabarits voient les deux champs, normalisés :

ChampTypeQuoi
.page.authorMap?Premier (ou seul) enregistrement d’auteur résolu, ou null
.page.authorsList[Map]Tous les enregistrements d’auteurs résolus — liste vide quand aucun

Chaque enregistrement porte chaque clé du registre — id, name, bio, avatar, links[] — pour qu’une signature par article puisse rendre directement l’avatar, le nom et les liens externes :

{{ if .page.author }}
<div class="byline">
  {{ if .page.author.avatar }}
  <img src="{{ .page.author.avatar }}" alt="" />
  {{ end }}
  <div>
    <p class="name">{{ .page.author.name }}</p>
    {{ if .page.author.bio }}<p class="bio">{{ .page.author.bio }}</p>{{ end }}
  </div>
</div>
{{ end }}
Note

Une valeur author: du frontmatter qui ne correspond à aucun id du registre retombe sur un enregistrement embryonnaire {id: "<typo>"}. Les gabarits qui lisent .page.author.name obtiennent une chaîne vide plutôt qu’un échec dur — la construction ne casse pas sur une faute de frappe, mais l’archive d’auteur est vide. Auditez .site.authors si un nom disparaît de façon inattendue.

Archives d’auteurs

Deux pages d’archive par registre d’auteurs sont émises, chacune conditionnée par un layout optionnel :

LayoutMotif d’URLCe qu’il liste
_default/author-list.html/authors/index.htmlChaque auteur avec au moins un article qui le référence
_default/author-page.html/authors/<id>/index.htmlLes articles d’un auteur, plus récent d’abord

Le layout author-list.html voit :

{{ .authors }}                  // List of records, in registry order
{{ .authors[0].name }}
{{ .authors[0].url }}           // "/authors/ed/"
{{ .authors[0].count }}         // 12
{{ .authors[0].pages }}         // their posts (already date-desc)

Le layout author-page.html voit :

{{ .author }}                   // single record
{{ .author.name }}
{{ .author.bio }}
{{ .author.count }}
{{ .author.pages }}             // posts, newest first

Les auteurs avec zéro article qui les référence sont omis des deux archives — cela empêche un registre [[authors]] partiellement rempli d’émettre des pages vides « voir tous les articles de Bob ».

.site.authors

Comme .site.tags et .site.categories, les mêmes enregistrements d’auteurs sont exposés à l’échelle du site pour usage dans n’importe quel gabarit :

{{ for a <- .site.authors }}
<a href="{{ a.url }}">{{ a.name }} ({{ a.count }})</a>
{{ end }}

Utile pour une liste de « contributeurs » dans un pied de page ou une page « à propos ».

Alias / redirections

Quand vous changez l’URL d’un article — en éditant son nom de fichier, en ajoutant un gabarit [permalinks] ou en renommant une section — chaque lien vers l’ancienne URL casse. Les alias arrêtent l’hémorragie sans réécritures côté serveur.

Ajoutez la (les) ancienne(s) URL au frontmatter de la nouvelle page :

---
title: Setting up a Scala 3 project
date: 2024-03-12
aliases:
  - /old-blog-name/setting-up-scala/
  - /2023/setting-up/
---

Juicer émet une petite page HTML statique à chaque URL listée. La page définit un <meta http-equiv="refresh"> vers l’URL canonique, plus un lien de repli visible au cas où le rafraîchissement meta serait bloqué :

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="refresh" content="0; url=/posts/setting-up-scala-3/">
  <link rel="canonical" href="https://example.com/posts/setting-up-scala-3/">
  <title>Redirecting…</title>
</head>
<body>
  <p>This page has moved. Redirecting to <a href="/posts/setting-up-scala-3/">/posts/setting-up-scala-3/</a>.</p>
</body>
</html>

C’est le défaut intégré. Il fonctionne sans aucun thème. Si vous voulez un autre balisage — style de marque, texte personnalisé, repli JavaScript — déposez un layout à layouts/_default/alias.html :

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="refresh" content="0; url={{ .target }}">
  <link rel="canonical" href="{{ .absTarget }}">
  <title>{{ .page.title }} — moved</title>
</head>
<body>
  <h1>This page has moved</h1>
  <p><a href="{{ .target }}">Continue to {{ .page.title }}</a></p>
</body>
</html>

Le layout personnalisé a accès à :

ChampQuoi
.targetURL canonique relative au site
.absTargetURL canonique absolue (baseURL + .target)
.page.<...>Chaque champ de l’enregistrement de la page canonique
.site.<...>Le contexte de site complet

Les alias acceptent une simple chaîne ou une liste :

aliases: /old-url/
aliases:
  - /old-url/
  - /even-older-url/
Note

Les alias ne sont pas la même chose que la réécriture d’URL. Chaque alias est un vrai fichier HTML statique dans l’arborescence de sortie — <dst>/old-url/index.html, <dst>/even-older-url/index.html, etc. C’est là tout l’intérêt : il fonctionne sur n’importe quel hôte statique (GitHub Pages, Cloudflare Pages, S3, Netlify…) sans configuration de règle de périphérie. Le coût est d’un minuscule fichier HTML par alias.

Cartes OpenGraph / Twitter

Quand un article est partagé sur les réseaux sociaux, la plateforme rend un aperçu en « carte » construit à partir d’un petit bloc de balises <meta> dans le <head> de la page. Juicer livre un builtin de gabarit {{ ogTags .page }} qui émet le bloc canonique en une ligne :

<head>
  ...
  {{ ogTags .page }}
</head>

Cet appel se déploie en :

<meta property="og:type" content="article" />
<meta property="og:title" content="The post title" />
<meta property="og:url" content="https://example.com/posts/the-post/" />
<meta property="og:description" content="The post summary." />
<meta property="og:image" content="https://example.com/img/hero.jpg" />
<meta property="og:site_name" content="My blog" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="The post title" />
<meta name="twitter:description" content="The post summary." />
<meta name="twitter:image" content="https://example.com/img/hero.jpg" />

Les balises dont le champ source est vide sont omises (vous n’obtenez pas un <meta property="og:image" content="" /> vide qui encombre la sortie).

Règles de résolution

Pour chaque valeur dont le builtin a besoin, il essaie les champs par ordre de priorité :

BaliseChaîne de résolution
og:title, twitter:title.page.ogTitle.page.title
og:description, twitter:description.page.ogDescription.page.description.page.summary
og:image, twitter:image.page.ogImage.page.image.site.ogImage.site.image
og:url.page.permalink (toujours présent — l’URL absolue que juicer a calculée)
og:site_name.site.title
twitter:cardsummary_large_image si une image a été résolue, sinon summary

Les URL d’image sont promues en absolu via le baseURL configuré — un /img/hero.jpg relatif au site devient https://example.com/img/hero.jpg. Les robots d’indexation rejettent typiquement les URL relatives dans og:image, donc cela compte.

Surcharges par page

Pour définir un titre ou un résumé de carte différent du titre et du résumé propres à la page, ajoutez ogTitle / ogDescription / ogImage au frontmatter de l’article :

---
title: A long, descriptive, SEO-targeted post title
summary: A long summary that's good for the page's own list-page meta…
ogTitle: A short, punchy title for social cards
ogDescription: A different summary that fits in a card preview.
ogImage: /img/posts/short-title-card.png
---
Note

Le thème juicerblog appelle {{ ogTags .page }} depuis son partials/head.html, donc chaque page obtient le bloc meta automatiquement sans que vous ayez à faire quoi que ce soit dans l’article lui-même. Les sites qui veulent contrôler le lieu de l’appel (ou le sauter pour certaines pages) peuvent supplanter le partial.

Le thème juicerblog

Juicer livre un thème de blogue par défaut sous themes/juicerblog/ qui exerce chaque fonctionnalité de cette page — plus la coloration syntaxique côté serveur, les signatures d’auteur, les badges de progression de série, une mince ligne de progression de lecture, des boutons de copie de bloc de code, et une paire de layouts accueil / archive. La référence complète du thème réside dans sa propre section : voir juicerblog pour l’aperçu et juicerblog · Configuration pour tous les boutons de configuration, conventions de frontmatter, mise en place de la coloration syntaxique et motifs de surcharge.

Le moyen le plus rapide de voir chaque fonctionnalité de blogue fonctionner ensemble est la démo fournie :

sbt 'juicerJVM/run serve -s docs/demos/juicerblog -L'

Cela lance un aperçu en direct de docs/demos/juicerblog/ — 9 articles datés couvrant juil.–déc. 2024, trois auteurs avec une co-signature multi-auteurs, une série de 3 articles, dateArchives + permaliens + alias, coloration syntaxique, tout le tralala. Touchez n’importe quel fichier markdown dans la source et la page se recharge en moins d’une seconde.

Où réside chaque fonctionnalité

Quand une fonctionnalité casse, voici par où commencer à chercher :

FonctionnalitéMoteurThème
Mots-clés / catégoriesApp.scala collectTaxonomy, renderTaxonomyArchivestag-list.html, tag-page.html
Paginationpaginate.scala, découpage de boucle de rendu dans App.scalapartials/pagination.html
DatesApp.scala parseDateString, formatDateLongpartials/post-meta.html
Temps de lectureApp.scala wordsOf, readingTimeOfPartout où .page.readingTime est utilisé
Assistant slugpackage.scala slugify, asciiFold(utilisé en interne pour les URL de mots-clés)

Recherche

Esc
pour naviguer pour ouvrir Esc pour fermer