Manuel de rédaction

Guides pour le nommage des fichiers, la syntaxe et les fonctionnalités utiles à la rédaction du site.

Articles et pages individuelles

Cette section concerne les content collections blog et pages.

Sidenotes

Pour activer les sidenotes, ajouter l’objet sidenote dans le frontmatter en précisant tout ou partie de ses options, les valeurs par défaut étant surlignées :

sidenotes:
label: number | symbol | none
toggle: true | false
visible: true | false
removeFootnotes: true | false
layoutOnly: true | false
  • label :
    • number : numérotation à la manière des notes de bas de page classiques.
    • symbol : astérisme.
    • none : permet de produire une note de marge (margin note).
  • toggle : sur mobile, l’apparence du label change et est cliquable pour changer la valeur de visible.
  • visible : visibilité de la sidenote sur mobile par défaut.
  • removeFootnotes : masquer la section « Notes » en bas de page après avoir généré les sidenotes.
  • layoutOnly : ne pas générer les sidenotes à partir des footnotes (ignorant les options précédentes) mais tout de même activer la disposition de page adaptée aux sidenotes, en vue d’une utilisation des composants.

Ces options affectent les footnotes écrites avec la syntaxe Markdown, qui sont transformées en sidenotes en JavaScript côté client.

RPG Tsukūru[^rpg-tsukuru] est un outil de création de jeux.
[^rpg-tsukuru]: Titre original : RPG ツクール.

Pour appliquer d’autres options à une sidenote précise, il est possible d’utiliser le composant <Sidenote> et de lui passer les options souhaitées. Si aucune option n’est passée, les options par défaut de <Sidenote> sont label: symbol, toggle: true et visible: false. Il existe également <MarginNote> qui est un raccourci pour label: none, toggle: false et visible: true.

Attention : En utilisant les composants <Sidenote> et <MarginNote>, il n’est pas possible d’utiliser des éléments de type bloc comme les paragraphes (<p>). Pour cela, il faut obligatoirement utiliser la syntaxe Markdown. Voir <Sidenote>.

/kitchen

Nommage des fichiers

Les ingrédients et les recettes partagent l’URL /kitchen/{slug}. Attention aux conflits.

Conventions typographiques

On sépare la partie décimale des nombres avec une virgule.

Une bouteille de 1,5 litre.

Le symbole d’unité est séparé par une espace insécable.

12,5 km, 12,50 €, 12,5 °C, 120 g.

Syntaxe

Dans une recette, les ingrédients doivent être un slug existant. La quantité, optionnelle, doit être une string si elle est précisée.

ingredients:
- [kale, 100g]
- [halloumi, "2"]
- olive-oil

Un élément gras en début de liste est considéré comme un sous-titre et transformé en petites capitales. Si la liste n’est pas numérotée, la puce est retirée et le titre est indenté en arrière.

- **Au four :** ajouter le <Ing slug="quinoa">quinoa</Ing> dans un plat à gratin.

Le composant <Ing> affiche une pop-up contenant les infos de l’ingrédient. La syntaxe est un raccourci de <Ingredient> et <IngredientInPopup>.

/gear

Nommage des fichiers

Le premier dossier représente la catégorie mais ne fait pas partie du slug :

src/content/gear/{catégorie}/{slug}.mdx

Par exemple, le fichier gear/phones/iphone-11.mdx peut être lu à l’adresse /gear/iphone-11.

/diary

Nommage des fichiers

Le chemin des fichiers est :

src/content/diary/{catégorie}/{année}/{slug}.mdx

Par exemple, le fichier diary/trips/2023/rodez-conques.mdx peut-être lu à l’adresse /diary/rodez-conques.

Date

Dans le frontmatter, la date est optionnelle. Si elle est absente, l’année donnée dans le chemin fait office de date approximative.

Listes de lieux

Par défaut, les lieux indiqués dans le frontmatter sont affichés dans une liste au début de la page et sont reliés à l’entrée, ce qui est utilisé à d’autres endroits du site.

places:
- occitanie/rodez
- occitanie/conques-en-rouergue

Le composant <PlacesList> permet d’afficher une liste similaire à un endroit choisi du document. Cependant, les lieux ne seront pas reliés à l’entrée comme s’ils étaient présents dans le frontmatter. Pour régler ce problème, les entrées utilisant <PlacesList> peuvent masquer la liste par défaut et y ajouter tous les lieux mentionnés.

---
places:
- paris/kodawari-yokocho
- paris/kodawari-tsukiji
hidePlaces: true
---
Voici des restaurants de ramen.
<PlacesList places={["paris/kodawari-yokocho", "paris/kodawari-tsukiji"]} />

Ressources

Paramètres de frontmatter

Le fichier src/content/config.ts (inaccessible car situé dans le submodule privé) contient la liste des content collections existantes et leur schéma. Voici son contenu :

src/content/config.ts
import { defineCollection, reference, z } from "astro:content"
import { PaletteName } from "$utils/design/palettes"
import { Layouts } from "$utils/design/layouts"
const sidenotesOptions = z.object({
label: z.enum(["number", "symbol", "none"]).default("none"),
toggle: z.boolean().default(false),
visible: z.boolean().default(true),
removeFootnotes: z.boolean().default(true),
layoutOnly: z.boolean().default(false),
})
// The following schemas define the shape of the frontmatter or entry data in each
// collection. Each key of `collections` matches a directory name in `src/content`.
export const collections = {
153 collapsed lines
blog: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string().optional(),
date: z.date(),
categories: z.array(z.string()).default([]),
tags: z.array(z.string()).default([]),
opengraph: image().optional(),
image: image().optional(),
imageAnchorTop: z.boolean().optional(),
cover: z.boolean().default(false),
license: z.string().optional(),
customLayout: z.boolean().default(false),
toc: z.boolean().default(false),
depth: z.onumber(),
draft: z.boolean().default(false),
palette: z.nativeEnum(PaletteName).default(PaletteName.default),
layouts: z.array(z.nativeEnum(Layouts)).default([Layouts.classic]),
sidenotes: sidenotesOptions.optional(),
games: z.array(z.string()).default([]),
books: z.array(z.string()).default([]),
movies: z.array(z.number()).default([]),
places: z.array(z.string()).default([]),
}),
}),
pages: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string().optional(),
parent: z.string().optional(),
seeAlso: z.array(z.string()).default([]),
opengraph: image().optional(),
image: image().optional(),
imageAnchorTop: z.boolean().optional(),
cover: z.boolean().default(false),
customLayout: z.boolean().default(false),
toc: z.boolean().default(false),
depth: z.onumber(),
draft: z.boolean().default(false),
palette: z.nativeEnum(PaletteName).default(PaletteName.default),
layouts: z.array(z.nativeEnum(Layouts)).default([Layouts.classic]),
sidenotes: sidenotesOptions.optional(),
}),
}),
diary: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
date: z.date().optional(),
end: z.date().optional(),
draft: z.boolean().default(false),
highlight: z.boolean().default(false),
image: image().optional(),
imageAnchorTop: z.boolean().optional(),
places: z.array(z.string()).default([]),
mapLat: z.number().optional(),
mapLng: z.number().optional(),
mapZoom: z.number().optional(),
hidePlaces: z.boolean().default(false),
customLayout: z.boolean().default(false),
}),
}),
portfolio: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string().optional(),
lede: z.string().optional(),
release: z.date().optional(),
client: z.string().optional(),
roles: z.array(z.string()).default([]),
moreRoles: z.array(z.string()).default([]),
tools: z.array(z.string()).default([]),
moreTools: z.array(z.string()).default([]),
link: z.string().optional(),
linkTitle: z.string().optional(),
image: image().optional(),
}),
}),
places: defineCollection({
schema: z.object({
title: z.string(),
id: z.string(),
status: z.enum(["todo", "done"]),
review: z.enum(["loved", "liked", "okay", "disliked"]).optional(),
hide: z.boolean().default(false),
}),
}),
gear: defineCollection({
schema: z.object({
title: z.string(),
etat: z.string(),
utilisation: z.string().optional(),
obtained: z.date().optional(),
obtainedInfo: z.string().optional(),
price: z.string().optional(),
clickable: z.boolean().default(false),
}),
}),
games: defineCollection({
type: "data",
schema: z.object({
title: z.string(),
slug: z.string().nullish(),
quickReview: z
.enum([
"Coup de cœur",
"Aimé",
"Sympa un moment",
"Whatever",
"Mitigé",
"Décevant",
"J'aime pas",
"Mauvais",
"Pas pour moi",
])
.nullish(),
review: z.string().nullish(),
firstPlayedYear: z.number().nullish(),
progress: z.string(),
multiplayer: z.array(z.enum(["En ligne", "Local", "Coop", "Versus"])),
myPlatforms: z.array(z.string()),
blocks: z.array(z.any()),
notionUrl: z.string(),
lastEditedTime: z.string(),
igdb: z.any().nullish(),
}),
}),
recipes: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
ingredients: z
.array(
reference("ingredients").or(z.tuple([reference("ingredients"), z.string().nullish()]))
)
.default([]),
tags: z.array(z.string()).default([]),
cover: image().optional(),
draft: z.boolean().default(false),
}),
}),
ingredients: defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
tags: z.array(z.string()).default([]),
icon: image().optional(),
}),
}),
}