louis-potevin.dev — Portfolio v2
Portfolio bilingue (FR/EN) construit avec Astro 7, Nova Design System, Three.js et Lenis. Accueil = un « voyage » : le scroll ne fait jamais défiler une page, il pilote une caméra 3D et l'animation continue. Chaque projet a sa propre scène sur-mesure.
Stack
- Astro 7 — SSG, i18n natif, zéro JS par défaut
@unkn0wndo3s/nova-design-system— composants + tokens (source unique du design)- Three.js — le moteur du voyage (accueil)
- Lenis — smooth scroll ; toutes les animations lisent le scroll depuis Lenis, d'où la sensation d'une seule courbe de mouvement
- Sass — les mixins typographiques NDS sont exposés via un
loadPath(voirastro.config.mjs)
Démarrer
npm install # .npmrc force legacy-peer-deps (requis par @lucide/astro avec Astro 7)
npm run dev # http://localhost:4321
npm run build # sortie statique dans dist/
npm run check # astro check (TS + diagnostics) - 0 erreur attendu
Node >= 22.12 (contrainte du NDS).
Structure
src/
├── data/ site.ts (profil, liens), projects.ts (projets localisés + stages du showcase NDS)
├── i18n/ index.ts (helpers locale/chemins/hreflang), ui.ts (dictionnaires FR/EN)
├── lib/seo.ts JSON-LD : Person, WebSite, BreadcrumbList, SoftwareSourceCode, ProfilePage
├── lib/smooth.ts Singleton Lenis · lib/scrub.ts - progression 0..1 des scènes épinglées
├── layouts/ BaseLayout.astro (head SEO complet, nav, footer, init Lenis)
├── components/
│ ├── scene/ voyage.ts - moteur de l'accueil · scene.ts - fond ambiant des sous-pages
│ ├── scenes/ InfraScene, BlueprintScene, SorterScene - scènes projet sur-mesure
│ └── showcase/ ComponentShowcase.astro - composants NDS "volants" (page projet NDS)
├── views/ La logique de chaque page (partagée entre locales)
└── pages/ Routes minces : / (FR, défaut) et /en/* (miroirs)
Règle i18n : une page = une view. Les routes (pages/ et pages/en/) ne font
qu'importer la view ; la locale est déduite de l'URL (getLocaleFromUrl).
/ = français (cible : recruteurs FR), /en/ = anglais. Pour inverser, changer
DEFAULT_LOCALE dans src/i18n/index.ts et i18n.defaultLocale dans astro.config.mjs.
Le voyage (accueil)
voyage.ts transforme la hauteur de scroll en timeline : les sections HTML sont des
overlays fixes, et le scroll avance une caméra sur une courbe Catmull-Rom à travers
nébuleuses, ceinture d'astéroïdes (une balise lumineuse par projet) et portail final.
- Scroll down → les astéroïdes dérivent et se percutent (collisions élastiques, étincelles additives).
- Scroll up → vortex gravitationnel : spirale vers la formation d'origine.
- Barre de progression + points de navigation latéraux (clic = seek animé).
- Les ancres (
/#about,/#contact…) deviennent des seeks sur la timeline. - Progressive enhancement : le même markup rend une page normale empilée sans JS,
sans WebGL ou avec
prefers-reduced-motion(la classehtml.cineman'est jamais posée).
Métadonnées NDS toujours à jour
src/lib/ndsMeta.ts : la liste des composants (et leur nombre) est parsée depuis
l'index d'exports du paquet installé, et la version est récupérée sur le registre
npm au moment du build (fallback : version installée si hors-ligne). La forge,
les textes de la home et la fiche projet NDS lisent tous cette source — plus aucun
chiffre en dur à maintenir.
/docs — la documentation technique
Une page dédiée (/docs, FR/EN) documente tout le fonctionnement du site en 13 chapitres,
avec sommaire sticky (scrollspy) et snippets extraits du vrai code source au build
(imports Vite ?raw + découpe par marqueurs — si un marqueur disparaît, le build échoue
au lieu de publier une doc périmée). Coloration syntaxique Shiki via astro:components.
Scènes projet sur-mesure
Toutes épinglées (section haute + stage sticky), pilotées par scrub.ts, fallback statique :
| Projet | Scène |
|---|---|
| nova-design-system | ComponentShowcase (« la forge ») — 3 actes : les tokens (vrais swatches) convergent, 8 vrais composants NDS défilent dans un tunnel 3D avec specs + snippet d'usage, la bibliothèque se fige en grille avec compteurs |
| nova-infra | InfraScene — le graphe Visiteur → Cloudflare → Apache/VPS se dessine au scroll, puis un paquet circule en continu |
| portfolio | BlueprintScene — le wireframe du site se trace (dashoffset) puis s'allume aux couleurs NDS |
| file-organizer | SorterScene — 20 fichiers éparpillés (seed déterministe) se rangent en colonnes au scroll |
SEO
hreflangfr / en / x-default + canonical sur chaque page- JSON-LD :
Person(@id partagé),WebSite,BreadcrumbListpar page,SoftwareSourceCodepar projet,ProfilePagesur /about - Open Graph + Twitter cards localisés (
og-fr.png/og-en.png) - Sitemap i18n (
@astrojs/sitemap) +robots.txt - Pas de meta
keywords(ignorées par Google, signal spam pour certains crawlers)
À faire côté déploiement
- Désactiver « Email Address Obfuscation » dans Cloudflare (Scrape Shield) :
sinon CF injecte
email-decode.min.js, casse le mailto sans JS et affiche « [email protected] » aux crawlers de recruteurs. public/og-fr.pngetpublic/og-en.png(1200×630) — visuels de partage.- Brancher le workflow Gitea Actions existant (
npm ci && npm run build, publierdist/). /aboutet/contactredirigent vers/#aboutet/#contact(config Astro) — les anciennes URL indexées ne cassent pas.- Tester le voyage sur un vrai GPU/trackpad et ajuster les poids
data-weightdes sections dansHomeView.astro(durée relative de chaque étape).