BEBahae Eddine
retour au blog
Ingénierie IA4 août 20266 min de lecture

Construire un portfolio auto-hébergé : zéro CDN, zéro compromis

Comment ce site a été construit — Next.js 15, Tailwind CSS v4, shadcn/ui, Framer Motion, polices auto-hébergées et une politique délibérée de zéro requête tierce.

Ce site est le premier vrai produit de la stack que j'utilise pour les clients chez XAI.ma. Il est entièrement auto-hébergé — pas de polices CDN, pas de Tailwind à l'exécution, pas d'icônes externes, pas de balises d'analyse qui rappellent à la maison. Cela ressemble à un choix esthétique. C'est en réalité un choix d'ingénierie.

La stack

  • Next.js 15 avec App Router, Server Components et Server Actions
  • TypeScript partout, mode strict activé
  • Tailwind CSS v4 avec la nouvelle configuration CSS-first @theme
  • shadcn/ui sur des primitives Radix UI
  • Framer Motion pour les apparitions au scroll, le marquee, les compteurs et les onglets en pilule
  • TanStack Query, Zustand, React Hook Form, Zod pour la récupération de données, l'état, les formulaires et la validation
  • Polices — Fraunces, Inter, JetBrains Mono — auto-hébergées via next/font

Le plus remarquable n'est pas que cette stack existe ; c'est que tout compile en quelques centaines de kilooctets et rend du HTML statique pour chaque route. Le blog est du MDX avec une couche de contenu sur mesure. Les projets sont des études de cas dans le même système. Rien n'est récupéré à l'exécution, à l'exception du formulaire de contact.

Pourquoi pas de CDN ?

Trois raisons, toutes pratiques plutôt qu'idéologiques :

Confidentialité. Chaque script tiers est une fuite de données. Quand une police se charge depuis les serveurs de Google, Google apprend que quelqu'un a visité ce site. Quand une analyse se charge depuis un SaaS, le SaaS construit un profil. Sur un site personnel avec zéro besoin de suivi, la valeur par défaut devrait être aucune requête externe, et il faut justifier chaque exception plutôt que l'inverse.

Fiabilité. Un lien CDN pourrit. Un domaine change. Une balise script casse votre page dans un navigateur que vous n'avez pas testé. Chaque dépendance externe est un point de défaillance unique que vous ne contrôlez pas et que vous découvrirez un dimanche. L'auto-hébergement signifie que la surface d'échec est exactement la surface que vous gérez.

Vitesse. Moins d'allers-retours. Les polices sont empaquetées, hachées et préchargées par Next au moment du build. Pas de feuille de style bloquante depuis une autre origine, pas de danse de preconnect, pas de requête DNS pour un hôte qui n'existe que pour servir une police. Le budget de performance est imposé par la construction, pas par la surveillance.

Il y a une quatrième raison qui compte pour le côté business de mon travail : les clients remarquent. Quand je livre un projet et que l'audit dit « zéro requête tierce, tous les Core Web Vitals dans le vert », c'est une fonctionnalité qu'ils peuvent mettre dans une proposition. La discipline est le produit.

Le terminal, le marquee et les petits détails

L'essentiel du savoir-faire visible est dans les détails. Le terminal — qui vit désormais dans le footer — est une vraie machine à états, pas une animation en boucle :

ts
// tape une fois, puis tiens — pas de boucle de re-frappe infinie
const tick = () => {
  setProgress((prev) => {
    if (prev.done) return prev;
    const current = LINES[prev.line];
    if (prev.chars < current.length) return { ...prev, chars: prev.chars + 1 };
    if (prev.line < LINES.length - 1) return { line: prev.line + 1, chars: 0 };
    return { line: LINES.length, chars: 0, done: true };
  });
};

Il tape d'abord le titre de la fenêtre, puis le corps, puis s'arrête avec un curseur clignotant. Cette séquence compte : une animation qui respecte l'attention du visiteur se lit comme délibérée. Les pastilles façon macOS dans l'en-tête du terminal sont un thème récurrent sur tout le site — la modale CV, les blocs de code, le terminal. La cohésion est sous-estimée.

Le mouvement réduit est respecté partout via useReducedMotion. Ce n'est pas une case à cocher ; c'est un test pour savoir si l'animation a été construite pour servir le contenu ou pour se servir elle-même.

Les décisions de build qui ont compté

Quelques choix ont façonné tout le reste, et ils valent la peine d'être écrits car ils se transfèrent directement au travail client.

Le contenu comme code. Chaque article de blog et chaque étude de cas de projet est un fichier MDX dans le dépôt. Le frontmatter pilote les métadonnées — titre, description, catégorie, statut de publication — et une petite couche de contenu lit le système de fichiers au moment du build. Le sitemap est généré depuis la même source de vérité. Cela signifie que les métadonnées SEO, les sitemaps, le temps de lecture et l'index du blog ne peuvent jamais se désynchroniser, car tout dérive d'un seul endroit.

ts
// la couche de contenu — une source de vérité pour les articles et les sitemaps
export function getAllPosts(): BlogPost[] {
  return listMdx(BLOG_DIR)
    .map((file) => {
      const raw = fs.readFileSync(path.join(BLOG_DIR, file), "utf8");
      const { data } = matter(raw);
      return { ...(data as PostFrontmatter), slug, readingTime: readingTime(raw) };
    })
    .filter((p) => p.published)
    .sort((a, b) => (a.date < b.date ? 1 : -1));
}

L'index du blog est un produit de données. La recherche, les filtres par catégorie et les pastilles de temps de lecture fonctionnent sur un cache TanStack Query avec les mêmes données initiales que celles rendues par le serveur. La page est instantanément interactive et n'affiche jamais d'état de chargement — améliorée progressivement par conception, pas en pensant après-coup.

La coloration syntaxique est une fonctionnalité de premier ordre. Les blocs de code des articles se rendent comme des fenêtres de terminal — pastilles, étiquette de nom de fichier, fond sombre — avec une vraie coloration syntaxique appliquée au moment du build. L'étape de coloration est un plugin remark/rehype, donc les articles sont rédigés en markdown simple et l'esthétique est gratuite.

Le CV est aussi un produit. Le bouton « Voir le CV » ouvre une modale avec un aperçu PDF intégré — paginé, avec téléchargement et fermeture sur Échap — plutôt que de déposer un fichier dans un nouvel onglet. Petit détail, mais il garde le visiteur dans le langage visuel du site.

SEO par défaut

Chaque page embarque des données structurées, une URL canonique et des balises Open Graph. Le fichier robots et le sitemap sont générés plutôt que maintenus à la main. Les budgets de performance sont imposés au niveau du framework : rendu statique par défaut, aucune récupération de données à l'exécution, polices auto-hébergées.

La raison pour laquelle cela compte pour un portfolio est la même que pour les clients : un site qui se classe bien et se charge vite est un site qui obtient la réunion. La bonne ingénierie et le bon SEO sont la même discipline vue sous deux angles — tous deux consistent à supprimer les frictions entre le visiteur et la réponse.

Et ensuite

Le blog que vous lisez est le premier des deux systèmes de contenu de ce site. Chaque projet a sa propre page d'étude de cas — objectifs, architecture, stack et leçons apprises. L'écriture va au-delà de l'ingénierie : j'ai publié sur les cas d'usage avec des fondateurs, du SEO au GEO, et sur la réalité du conseil en IA, parce que les meilleurs ingénieurs sont ceux qui savent expliquer le pourquoi aussi clairement que le comment.

Le site lui-même est un artefact vivant. Il continuera d'évoluer — nouveaux projets, nouveaux articles, nouvelles expériences — mais les contraintes restent : rapide, privé, auto-hébergé, et construit avec une stack que je mettrais devant n'importe quel client.

Si vous construisez quelque chose de similaire et que vous voulez un avis tranché, contactez-moi. C'est exactement le genre de travail que nous livrons chez XAI.ma.

Ça vous a plu ? Parlons-en.

Je dirige des équipes qui conçoivent et déploient des systèmes d'IA — ouvert au conseil, aux collaborations de recherche, ou à être recruté pour prendre la tête de la vôtre. Ingénierie IA, RAG, systèmes multi-agents et transformation digitale.

Me contacter