/ Colophon

Comment ce site est construit : une exposition technique

Cette page est elle-même une pièce de portfolio. Aucune étape de build que vous ne puissiez lire, aucune dépendance que vous ne puissiez nommer. Voici exactement ce qui le fait tourner.

Le framework

Le site tourne sur Pulsar, mon propre framework en PHP 8.5 et Rust. L'injection de dépendances est compilée, le conteneur se résout donc au build et il n'y a aucune réflexion sur le chemin critique. Les couches domaine, application et infrastructure restent honnêtement séparées, et les modules critiques portent une vérification formelle avec Creusot.

Le système de tokens

Tout le visuel dérive d'un seul jeu de propriétés personnalisées, les tokens Precision Noir. Trois accents d'identité, permutés par section avec un seul attribut. Aucune couleur en dur ne vit dans un composant.

#0039CBDéveloppement
#D4A843Photographie
#2D8F47Exploration
1--accent, permuté
theme.css
/* one accent, re-pointed per section */[data-section="photography"] {  --accent: var(--color-photo);  --accent-glow: var(--color-photo-glow);}

Performance et accessibilité

Deux polices variables auto-hébergées, préchargées, avec font-display swap. Images responsives en AVIF puis WebP, dimensions explicites pour que rien ne bouge. Le JavaScript est minimal, différé, et le contenu principal fonctionne sans lui. Les objectifs sont réels : LCP sous 2,5 s, CLS sous 0,1, INP sous 200 ms.

Le contraste respecte WCAG 2.2 AA dans les deux thèmes et sur les galeries en noir absolu. Chaque élément interactif a un anneau de focus visible, le mouvement s'efface sous reduced-motion, et un lien d'évitement est la première chose qu'atteint le clavier.

Hébergement

AWS EC2 avec Nginx et PHP-FPM 8.5. HTTP/3 et mise en cache en périphérie via CloudFront, courrier transactionnel via SES, certificats par ACM. Rien d'exotique, tout est volontairement ennuyeux.

En-têtes de sécurité recommandés

Livrés en périphérie. Voici le jeu, en exhibit.

Durcissement des formulaires

Les formulaires de contact et de réservation sont défendus en couches : un champ pot de miel caché que seuls les bots remplissent, un piège temporel qui rejette les envois sous 2,5 s, une validation et un échappement côté serveur, une limitation de débit par IP, et un défi hCaptcha tenu en réserve pour tout ce qui paraît encore automatisé. Les formulaires indiquent aussi clairement qu'ils servent aux demandes de projet et aux questions, pas à la prospection à froid.

headers.conf
Content-Security-Policy: default-src 'self';  img-src 'self' data:; font-src 'self';  script-src 'self'; style-src 'self'Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: geolocation=(), camera=(), microphone=()

Images et polices

Chaque image est livrée en AVIF avec un repli WebP et JPEG, dans un <picture> responsive aux dimensions explicites pour que rien ne bouge au chargement. L'image héros porte fetchpriority="high" ; tout ce qui est sous la ligne de flottaison est lazy. Les polices sont en woff2 auto-hébergé, préchargées et permutées, donc aucune requête tierce sur le chemin critique.

picture.html
<span class="media" style="--ar: 4 / 5">  <picture>    <source type="image/avif" srcset="portrait-480.avif 480w, portrait-960.avif 960w" sizes="(max-width: 600px) 100vw, 480px">    <source type="image/webp" srcset="portrait-480.webp 480w, portrait-960.webp 960w" sizes="(max-width: 600px) 100vw, 480px">    <img src="portrait-960.jpg" width="960" height="1200" alt="Portrait studio, lumière clé douce" loading="lazy" decoding="async">  </picture></span>
fonts.css
/* preloaded in <head>, swapped, self-hosted woff2 */@font-face {  font-family: 'Montserrat';  src: url(/fonts/montserrat-var.woff2) format('woff2-variations');  font-weight: 100 900; font-display: swap;}