~/blog / site-sans-framework-statique
⚙️ craft & architecture

J'ai construit ce site sans aucun framework, voici pourquoi (et comment)

Luc Del Beato 11 juin 2026 10 min de lecture

Le site que vous lisez en ce moment n'a pas de React, pas de Vue, pas de Next, pas de Svelte. Aucun bundler pour les pages, aucun runtime, aucun node_modules de 400 Mo. Juste du HTML, du CSS, et un fichier de JavaScript de soixante lignes. Et ce n'est pas de la nostalgie : c'est un choix d'architecture que j'assume, et que j'expliquerais à n'importe quel CTO. Voilà pourquoi, et exactement comment.

TL;DR

La confession qui fâche

Je vais commencer par le truc impopulaire : pour 90 % des sites qu'on construit aujourd'hui, le framework est de trop. On a pris l'habitude de démarrer chaque projet par npx create-something, d'installer deux cents dépendances, de configurer un bundler, un linter, un serveur de dev avec hot-reload… pour afficher, au bout du compte, du texte et quelques boutons. On a confondu « faire un site web » avec « faire une application web ». Ce n'est pas la même discipline.

Ce site, lucdelbeato.fr, et celui de mon produit, gerer.ai, sont tous les deux du HTML/CSS/JS pur. Pas par paresse, par exigence. Quand on a vingt ans de web derrière soi, on finit par mesurer une chose : la complexité qu'on ajoute aujourd'hui, c'est la dette qu'on payera dans trois ans. Alors j'ajoute le moins possible.

La règle que je m'applique : le site le plus rapide, le plus durable et le plus souverain est celui qui embarque le moins de JavaScript possible. Parfois, zéro.

Pourquoi statique-sans-framework pour un site de marque

Quatre raisons, et aucune n'est idéologique. Ce sont des propriétés mesurables.

1. La vitesse, gratuite et imbattable

Quand le serveur n'a qu'à renvoyer un fichier HTML déjà écrit, le TTFB (time to first byte) est quasi instantané : pas de rendu serveur, pas d'hydratation, pas de waterfall de chunks JavaScript à télécharger puis à exécuter avant que la page soit utilisable. Le navigateur reçoit du HTML, il l'affiche. C'est tout.

Résultat concret : les Core Web Vitals sont au vert sans que j'aie à « optimiser » quoi que ce soit. Pas de Largest Contentful Paint plombé par un bundle de 300 Ko, pas de Cumulative Layout Shift causé par une hydratation tardive, pas de Total Blocking Time parce qu'il n'y a presque pas de JS à exécuter. La performance n'est pas une couche qu'on ajoute après, c'est une conséquence directe de l'architecture.

2. La durabilité : ça marchera encore dans cinq ans

Voici un test mental que je fais sur tout projet : si je reviens dessus dans cinq ans, est-ce que ça se relance ? Avec un projet framework, la réponse est souvent « non, sans une journée de souffrance ». Les versions ont bougé, le bundler est déprécié, une dépendance transitive a disparu de npm, le lockfile pointe vers des packages qui n'existent plus. C'est le dependency rot, et il est silencieux jusqu'au jour où vous en avez besoin.

Du HTML, du CSS et du JS vanilla, eux, ne pourrissent pas. Le standard du web est rétrocompatible de façon quasi religieuse : une page écrite proprement aujourd'hui s'affichera dans le navigateur de 2031. Il n'y a littéralement rien à « builder », donc rien qui puisse casser au build. C'est la définition de la boring technology, et c'est une fonctionnalité, pas un défaut.

3. Sécurité & souveraineté : aucune surface, aucun runtime

C'est l'argument qui me tient le plus à cœur. Un site sans node_modules, c'est un site sans chaîne d'approvisionnement npm à sécuriser. Pas de package compromis qui injecte un mineur de crypto ou un voleur de tokens dans votre build, souvenez-vous de event-stream, de node-ipc, des centaines de typosquats. Quand vos dépendances de production se comptent sur zéro doigt, votre surface d'attaque côté supply-chain est nulle.

Pas de runtime non plus : il n'y a pas de processus Node qui tourne, donc pas de CVE serveur à patcher en urgence, pas de fuite mémoire à surveiller. Et comme le livrable est une poignée de fichiers, je peux l'héberger n'importe où : un bucket, un CDN, ou, c'est mon cas, un petit VPS à 3 € par mois physiquement en France. La souveraineté des données commence par là : savoir exactement où vivent les octets, et ne dépendre de personne pour les servir.

4. Le coût : essentiellement zéro

Pas de fonction serverless facturée à l'invocation, pas de build minutes qui explosent, pas de plan « Pro » d'une plateforme d'hébergement pour avoir le droit à un domaine custom. Du statique se sert pour quelques euros par mois, voire gratuitement. Pour un site de marque qui doit juste exister, vite et bien, c'est l'équation parfaite.

💡
Le bonus oublié : la transparence. Sur un site statique, view-source, c'est la vérité. Ce que vous lisez dans l'onglet « source » est exactement ce qui s'exécute. Pas de DOM virtuel reconstruit, pas de code minifié illisible généré par un transpileur. Pour un site qui parle de souveraineté et de sécurité, cette honnêteté du code compte.

Comment c'est vraiment fait ici

Assez de principes. Voici l'anatomie réelle de ce site, brique par brique.

Du HTML sémantique, point

Chaque page est du HTML écrit à la main : <header>, <nav>, <article>, <h2>, <blockquote>, <footer>. Le balisage décrit la structure du document, ce qui donne gratuitement l'accessibilité, le SEO et un rendu correct même si le CSS ne charge pas. Un article de blog, fondamentalement, c'est un document. Le traiter comme tel, plutôt que comme un arbre de composants, simplifie tout.

Un design system en CSS, avec des tokens

Pas de soupe de classes utilitaires, pas de CSS-in-JS, pas de Tailwind. Un seul fichier, site.css, organisé autour de custom properties CSS, mes tokens de design. Couleurs, espacements, rayons, typographies : tout est déclaré une fois en haut, et réutilisé partout. Changer l'accent mint du site, c'est modifier une seule variable.

:root{
  --bg:#0b0f14; --ink:#e8eef2; --dim:#8aa0ab;
  --mint:#5eead4; --amber:#fbbf24;
  --space:1.25rem; --radius:14px;
  --mono:"JetBrains Mono", ui-monospace, monospace;
}
.btn.mint{ background:var(--mint); color:#06251f; border-radius:var(--radius); }

Les custom properties, c'est ce qui rend le CSS natif aussi puissant qu'un design system de framework, sans le framework. Le navigateur les comprend nativement, elles cascadent, elles sont thématisables. On a tout ce qu'il faut depuis des années ; il suffisait d'arrêter d'attendre qu'un outil le réinvente.

~60 lignes de JavaScript vanilla

Le site a quelques touches interactives, et chacune tient en quelques lignes de vanilla, sans dépendance :

L'IntersectionObserver mérite un mot : c'est l'API qui a tué le besoin de bibliothèques d'animation au scroll. On observe les éléments, et quand ils croisent le viewport on ajoute une classe CSS qui déclenche la transition. Trois lignes, zéro dépendance, soixante images par seconde.

const io = new IntersectionObserver((entries) => {
  for (const e of entries) if (e.isIntersecting) {
    e.target.classList.add('in');   // le CSS fait le reste
    io.unobserve(e.target);
  }
}, { threshold: 0.12 });
document.querySelectorAll('.reveal').forEach(el => io.observe(el));

L'i18n maison : snapshot du DOM français, swap vers l'anglais

Le bilinguisme est le seul endroit qui demande un peu d'astuce. Le FR est la langue par défaut : il est écrit directement dans le HTML, donc visible même sans JS et indexé tel quel. Chaque nœud de texte porte un attribut data-i18n="clé", et un dictionnaire window.I18N_EN donne la traduction anglaise par clé.

Au premier chargement, le script prend un snapshot du DOM français (pour pouvoir revenir en arrière sans rechargement), puis, si l'utilisateur choisit EN, il remplace le textContent, ou l'innerHTML pour les nœuds qui contiennent des balises inline, marqués data-i18n-html. Le choix est persisté en localStorage, et la langue du navigateur (navigator.language) sert de valeur initiale. Voici le cœur, débarrassé du décor :

const FR = new Map();  // snapshot de l'original
document.querySelectorAll('[data-i18n],[data-i18n-html]').forEach(el => {
  const html = el.hasAttribute('data-i18n-html');
  FR.set(el, html ? el.innerHTML : el.textContent);
});

function setLang(lang){
  const dict = window.I18N_EN || {};
  document.querySelectorAll('[data-i18n],[data-i18n-html]').forEach(el => {
    const key  = el.getAttribute('data-i18n') || el.getAttribute('data-i18n-html');
    const html = el.hasAttribute('data-i18n-html');
    const val  = lang === 'en' ? dict[key] : FR.get(el);   // EN du dict, FR du snapshot
    if (val == null) return;
    if (html) el.innerHTML = val; else el.textContent = val;
  });
  document.documentElement.lang = lang;
  localStorage.setItem('lang', lang);
}

const initial = localStorage.getItem('lang')
  || (navigator.language.startsWith('en') ? 'en' : 'fr');
setLang(initial);

C'est tout. Pas de react-intl, pas de fichiers .po, pas de build d'extraction. Le coût ? Je dois écrire chaque traduction à la main et garder le dictionnaire synchrone. C'est un vrai travail manuel, et c'est un compromis que j'assume pour deux langues. À dix langues, je changerais d'approche.

Les polices : Google Fonts, et l'honnêteté du « pas encore »

Je charge les polices via Google Fonts, avec preconnect. Soyons honnêtes : c'est le seul endroit où ce site dépend encore d'un tiers américain. La prochaine étape vers la souveraineté complète, c'est de self-héberger les fontes, télécharger les .woff2, les servir depuis mon propre domaine, et couper ce dernier lien externe. C'est sur la liste. Je préfère le dire plutôt que prétendre à une pureté que le site n'a pas encore atteinte.

Le déploiement : un petit script Perl/sed qui réécrit les chemins

Le seul « outillage » du projet est un script de déploiement d'une poignée de lignes. En dev sous MAMP, mes chemins sont préfixés par /lucdelbeato.fr/ ; en production, le site vit à la racine du domaine. Le script fait une passe de sed pour réécrire ces chemins, et rsync le tout vers le serveur. Pas de pipeline CI, pas d'étape de build, une réécriture de chaînes et une copie de fichiers.

# déploiement, réécrire les chemins dev → prod, puis pousser
perl -pi -e 's{/lucdelbeato\.fr/}{/}g' dist/**/*.html
rsync -az --delete dist/ deploy@vps-fr:/var/www/lucdelbeato.fr/

C'est volontairement primitif. Un script que je peux lire en entier en dix secondes ne me trahira jamais à 2 h du matin. C'est aussi ça, la boring tech : préférer l'outil que je comprends complètement à celui qui fait tout sauf ce que j'attends ce jour-là.

Soyons justes : quand un framework EST le bon choix

Je ne suis pas dogmatique, et ce serait malhonnête de l'être. Il y a des cas où un framework n'est pas un luxe mais la bonne réponse :

La règle, donc, n'est pas « jamais de framework ». C'est « faire correspondre l'outil au problème ». Un site de contenu, ce sont des documents : du HTML statique est l'outil exact. Une application, c'est de l'état : un framework est l'outil exact. Le péché, ce n'est pas d'utiliser React, c'est de l'utiliser pour afficher une page « à propos ».

Les compromis honnêtes du sans-framework

Tout choix a un prix, et je refuse de vous vendre une solution sans en montrer la facture :

Le bon réflexe, c'est de regarder ces lignes en face avant de choisir. Pour ce site, la colonne « coûts » est minuscule et la colonne « bénéfices » est énorme. Pour votre app SaaS, ce sera peut-être l'inverse. C'est exactement le point.

Ce que je retiens

Cette philosophie n'est pas réservée à un blog perso. C'est exactement le même raisonnement que j'applique à l'infra de Gérer.ai : réduire la surface, supprimer les dépendances inutiles, savoir où vivent les octets, et préférer ce qu'on comprend entièrement à ce qui « marche par magie ». Le statique n'est pas un retour en arrière, c'est une discipline.

// gerer.ai

La même philosophie, appliquée à l'IA souveraine

Gérer.ai aussi est construit sans framework, statique, rapide, souverain. Et le produit pousse l'idée plus loin : des agents IA déployés sur votre infra, avec des modèles open-source auto-hébergés. Réduire la surface, maîtriser où vivent les octets, ne dépendre de personne.

Découvrir Gérer.ai
L
Luc Del Beato

Senior Lead Engineer, ~20 ans de web. Do-er passionné de résolution de problèmes, de belle architecture et d'automatisation ; les agents IA, c'est ma direction. Mon parcours →