← Blog

Sous le capot : construire vincentaugerot.com avec Astro

Comment j’ai conçu un site perso bilingue qui sert aussi de démo : statique d’abord, sans framework côté client, accessible, et bientôt un contrôleur IA qui répondra à partir de mon propre contenu.

Cloud4 minEN · FR

L’itinéraire

Un CV liste des stations, jamais le trajet. Je voulais un site où un recruteur ou un client voit en dix secondes comment mes compétences se relient, puis explore n’importe quel arrêt. D’où la métaphore du plan de métro : les lignes sont des compétences, les stations des postes, les correspondances les moments où tout se croise.

Cinq lignes structurent tout le site : Backend .NET, Leadership, Cloud, Entrepreneuriat et IA. Chaque expérience et chaque projet déclare les siennes, et c’est cette donnée qui dessine le plan, colore les badges et relie les pages entre elles.

Astro, Markdown et zéro framework côté client

Astro génère du HTML statique et n’envoie aucun JavaScript par défaut. J’ai poussé la logique jusqu’au bout : le site n’utilise aucun framework côté client, donc aucun îlot à hydrater. Les rares comportements interactifs (le filtre du plan, les panneaux split-flap, le choix de langue) sont de petits scripts TypeScript, souvent sous forme de custom elements, qu’Astro regroupe et minifie au build.

Articles, expériences et projets sont des fichiers Markdown dans des content collections typées. Le frontmatter est validé par Zod au build : une date de fin antérieure à la date de début, une ligne inconnue ou une station hors du plan font échouer la compilation au lieu de casser une page en production.

export const experienceSchema = z
  .object({
    company: z.string(),
    start: z.coerce.date(),
    end: z.coerce.date().optional(),
    lines: z.array(lineSchema).min(1),
    mapStation: mapStationSchema,
    // ...
  })
  .refine((data) => !data.end || data.end >= data.start, {
    message: 'end must be after start',
    path: ['end'],
  });

Ces schémas ont eux-mêmes leurs tests Vitest. Le contenu est la donnée principale du site, il mérite le même soin que le code.

Le plan du réseau

Le plan n’est pas une image : c’est un SVG calculé au build à partir du frontmatter. Chaque expérience porte une position sur l’axe du temps et la liste de ses lignes. Une station posée sur une seule ligne devient un cercle. Posée sur plusieurs, elle devient une correspondance, dessinée comme une pilule verticale qui traverse les voies.

mapStation:
  x: 540 # 60 + (année - 2010) × 60
  lanes: [backend, lead]

Ajouter un poste revient donc à écrire un fichier Markdown. Le plan, la frise et les liens se mettent à jour seuls.

Côté interaction, j’ai suivi l’amélioration progressive. Sans JavaScript, chaque station est un simple lien vers sa page, et tous les résumés sont listés sous le plan. Avec JavaScript, cliquer une station filtre les résumés pour n’afficher que le sien, et cliquer à nouveau les affiche tous. Pour qui préfère le texte à un schéma, une vue liste remplace le plan en un clic, et ce choix est mémorisé.

Le tableau des départs

Les panneaux à palettes (split-flap) de l’accueil et de la page projets sont du texte rendu côté serveur. Le script ne fait que faire défiler des caractères aléatoires avant d’afficher le bon, cellule par cellule, avec un léger décalage pour imiter le mécanisme.

L’animation respecte prefers-reduced-motion : dans ce cas, le texte s’affiche directement. Sur l’accueil, le panneau qui fait tourner mes différentes casquettes a aussi un bouton pause, car une rotation automatique reste du mouvement, même lente. Et toutes les facettes restent listées pour les lecteurs d’écran, qui n’ont pas à attendre l’animation.

Bilingue dès la conception

Je travaille en français et en anglais au quotidien, le site aussi. Traverser le pont dans l’en-tête change de langue et mène directement à la page équivalente, pas à l’accueil. Sous le capot, c’est le routage i18n natif d’Astro, avec des balises hreflang sur chaque page et un x-default pour les moteurs de recherche.

// astro.config.mjs
export default defineConfig({
  i18n: {
    locales: ['en', 'fr'],
    defaultLocale: 'en',
    routing: {
      prefixDefaultLocale: true,
      // La page racine détecte la langue elle-même.
      redirectToDefaultLocale: false,
    },
  },
});

La racine du site ne redirige pas aveuglément vers l’anglais. Elle lit les langues préférées du navigateur et envoie vers la première que le site propose : un navigateur réglé en fr-BE arrive sur la version française. Sans JavaScript, une redirection de secours et deux liens visibles prennent le relais.

Accessibilité

La signalétique de transport est pensée pour la lisibilité, ce site aussi. Quelques choix concrets :

  • Des couleurs en deux versions. Les couleurs des lignes sont pensées pour les tracés, et certaines sont trop claires pour du texte. Chaque ligne a donc une couleur de texte dédiée, assombrie quand il le faut, qui atteint au moins 4,5:1 de contraste (WCAG 2.1 AA).
  • Le code aussi. Le thème de coloration syntaxique github-dark affichait les commentaires sous le seuil AA. Je suis passé à github-dark-default, qui le respecte.
  • Le clavier partout. Chaque station du plan est focusable. Comme un lien SVG ne prend pas bien un outline, l’anneau de focus est dessiné directement sur la forme de la station.
  • Le mouvement sous contrôle. Réduction du mouvement respectée partout, y compris pour le défilement doux vers les résumés du plan.

Hébergement

Le site est entièrement statique et hébergé sur Cloudflare. Pas de serveur à maintenir, pas de base de données : un build produit des fichiers HTML, CSS et quelques scripts, servis au plus près du visiteur.

Prochain arrêt : un assistant RAG

Le prochain arrêt, c’est un contrôleur : un assistant qui répondra aux questions sur mon parcours, uniquement à partir du contenu de ce site et avec les liens vers les sources. Le Markdown déjà structuré et validé en est la base naturelle. Il fera l’objet de son propre article, une fois construit.

Terminus. Merci d’avoir fait le trajet.