Files
annu-kute-ced/doc2sveltekit-transition/recette-sveltekit-playbook-agent.md
T
sucupira d6efb6736f Réécriture SvelteKit statique (Svelte 5 runes + TS, adapter-static)
- Données PeerTube (GADE), Castopod (KUTE) et Mastodon (BOKANTE) bakées au
  build via +page.server.ts (throttle + retry 429 + déduplication)
- Charte OKI complète : tokens, thème sombre par défaut (clair opt-in,
  anti-FOUC), Archivo/Inter self-hébergées, flag-bar, sprite SVG (zéro emoji
  en interface, zéro Font Awesome/CDN)
- i18n FR/EN par routage [[locale]], bundles JSON, hreflang, %lang% serveur
- Pages : accueil, /video/[uuid] (embed, téléchargements, partage,
  commentaires, JSON-LD VideoObject), /categories/[id], /recherche (index
  JSON baké, recherche client), /direct (live + annonce multi-fuseaux),
  /dons, /mentions-legales, /offline + 404 kréyòl
- Motion gwoka : KineticText (scrub view()), ScrollProgressBar, FlagChip,
  reveal syncopé, View Transitions, gate prefers-reduced-motion unique
- PWA : manifest + Workbox generateSW (fallback /offline/), registerSW
  statique
- Sécurité : CSP par page en <meta> (SHA-256 des inline via
  scripts/postbuild-csp.mjs) + headers globaux (_headers Cloudflare et
  .htaccess o2switch), redirections des anciennes URLs PHP
- Doc : docs/DEPLOIEMENT-SVELTEKIT.md
2026-07-23 00:49:02 -04:00

212 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RECETTE SVELTEKIT — PLAYBOOK POUR AGENT DE CODE
### Transformer un site web classique en expérience de niveau Awwwards
> **Nature de ce document :** instructions opérationnelles destinées à un agent de code (Kimi CLI ou équivalent). Chaque règle est impérative et vérifiable. Ce playbook est distillé de l'analyse de 15 sites primés (Zentry, Igloo.inc, Immersive Garden, Noomo, Apple AirPods Pro, KPR, HAPE, Kriss.ai, Studio375, animejs.com, etc.). Il est volontairement autonome : tu n'as besoin d'aucun autre document pour l'appliquer, sauf si l'utilisateur fournit une charte de marque (qui alors **prime sur ce document** pour tout ce qui est couleurs, typographie, voix et iconographie).
---
## 0. MISSION
Transformer un site vitrine « classique » (HTML statique, WordPress, ou SPA sans direction artistique) en un site SvelteKit à forte identité visuelle et motion design maîtrisé, **sans sacrifier performance, accessibilité ni SEO**. Le résultat doit fonctionner parfaitement sur mobile 4G et matériel bas de gamme.
**Interdictions absolues :**
- Ne jamais installer de dépendance sans l'importer et l'utiliser.
- Ne jamais désactiver le SSR globalement pour « faire marcher » une lib client.
- Ne jamais animer autre chose que `transform` et `opacity` en JS.
- Ne jamais livrer une page dont le contenu est invisible sans JavaScript.
- Ne jamais réintroduire de service tiers (fonts, CDN, analytics) si le projet affirme une doctrine de souveraineté — tout asset se self-héberge.
---
## 1. LES 5 PRINCIPES (ADN commun des sites primés)
**P1 — Le scroll est le moteur de navigation principal.** Une seule instance Lenis au layout racine, partagée via contexte Svelte, cadencée par le ticker GSAP unique. Jamais de double boucle rAF.
**P2 — Bake au build, pas au runtime.** Toute valeur qui ne dépend pas d'une entrée utilisateur en direct est précalculée hors-ligne : bruit génératif baké en textures, caméras 3D bakées dans Blender (rig deux-Empty), animations squelettiques exportées en clips GLTF, textures compressées KTX2 via gltf-transform, images responsive générées au build (vite-imagetools).
**P3 — DOM d'abord, WebGL seulement où ça paie.** Zentry a produit des effets « 3D » primés en pur DOM (clip-path, preserve-3d, masks). Réserver WebGL à : systèmes de particules, simulations de fluide, vrais assets 3D à caméra contrôlée. Un flipbook canvas 2D (pattern Apple) couvre la plupart des besoins de « produit qui tourne au scroll ».
**P4 — Le motion se designe avant les pages.** Définir d'abord les tokens de mouvement (courbes, durées, staggers) en custom properties CSS, puis les composants. Toute durée/courbe hardcodée dans un composant est une erreur.
**P5 — Chaque état est un moment designé.** Loader, 404, état vide, hors-ligne : autant de touchpoints de marque. Le 404 générique du framework est interdit.
---
## 2. WORKFLOW EN 7 PHASES
### Phase 1 — AUDIT (toujours commencer ici)
- [ ] Inventorier pages, sections, assets, liens externes, formulaires du site source.
- [ ] Mesurer la stack existante (headers HTTP, poids des assets, dépendances tierces).
- [ ] Identifier les bugs de production (images cassées, mixed content, liens morts) et les lister AVANT toute refonte — une migration ne doit jamais masquer un bug existant.
- [ ] Identifier les contradictions doctrine/outillage (ex. discours anti-GAFAM + Google Fonts).
- [ ] Produire un court rapport d'audit et le faire valider avant de coder.
### Phase 2 — TOKENS & THÈME
- [ ] Créer `app.css` avec toutes les couleurs en `oklch()` (ou valeurs de la charte si fournie), typographies, échelle d'espacement, rayons, **tokens de motion** (courbes + durées).
- [ ] Déclarer le mapping Tailwind via `@theme inline` pour que les utilitaires référencent les variables.
- [ ] Thème via `data-theme` sur `<html>`, appliqué côté serveur dans `app.html` pour éviter tout FOUC.
- [ ] Fonts : woff2 self-hébergées dans `static/fonts/`, `@font-face` avec `font-display: swap`, `preload` de la display uniquement.
### Phase 3 — ARCHITECTURE
```
src/routes/
├── +layout.svelte ← SmoothScrollProvider + CursorTracker (si pertinent)
├── (site)/ ← pages de contenu (SSR complet)
│ ├── +page.svelte
│ └── [section]/[slug]/
├── (immersive)/ ← layout group : canvas lourds isolés ici
│ └── experience/+page.svelte
└── +error.svelte ← 404 designé (P5)
```
- [ ] Layout groups pour isoler tout contexte WebGL des pages de contenu.
- [ ] Imports dynamiques (`await import('three')`) derrière `{#if browser}` pour tout ce qui dépasse ~50 KB.
- [ ] `prerender = true` sur toutes les routes statiques ; adapter `adapter-static` ou `adapter-cloudflare` selon l'hébergement cible.
- [ ] Conserver strictement les URLs existantes (redirections 301 si renommage).
### Phase 4 — COMPOSANTS (bibliothèque de référence, §5)
- [ ] Installer uniquement les composants pertinents pour CE site — la bibliothèque n'est pas un package à copier en entier.
- [ ] Chaque composant livré avec : cleanup dans `$effect`, garde `prefers-reduced-motion`, aucune fuite de listener.
### Phase 5 — MOTION
- [ ] Transitions d'entrée/sortie de composants : transitions Svelte natives.
- [ ] Scrub/pinning lié au scroll : GSAP ScrollTrigger (import dynamique), OU `animation-timeline: view()` natif avec fallback ScrollTrigger pour Safari ancien.
- [ ] Micro-physics (curseur, springs) : Anime.js v4 imports modulaires (`animejs`, `animejs/text`) ou spring custom de 15 lignes.
- [ ] Vérifier le budget : ≤ 8-12 tweens concurrents sur mobile bas de gamme ; culler les animations hors viewport.
### Phase 6 — PERFORMANCE (budgets §4)
- [ ] Pipeline assets : `gltf-transform optimize --compress meshopt --texture-compress ktx2` pour tout GLB ; AVIF/WebP responsive pour les images.
- [ ] Media hero ≤ 400 KB avant interaction ; séquences d'images avec chargement conditionné par `navigator.connection`.
- [ ] Mesurer sur profil mobile 4G (CPU ×4, réseau « Fast 4G ») : LCP < 2,5 s, INP < 200 ms, CLS < 0,05.
### Phase 7 — ACCESSIBILITÉ & DÉGRADATION (§6)
- [ ] La ladder de dégradation complète et testée.
- [ ] Revue clavier complète (tab order, focus visible, skip-link).
- [ ] Contenu canvas doublé en DOM sémantique.
---
## 3. ORCHESTRATION D'ANIMATION — RÈGLES SVELTE 5
| Besoin | Outil | Règle |
|---|---|---|
| Entrée/sortie composant, listes | Transitions Svelte natives | Zéro dépendance, SSR-safe |
| Scrub scroll, pinning | GSAP + ScrollTrigger | Import dynamique, init dans `$effect`, kill au cleanup |
| Springs, stagger, split text | Anime.js v4 modulaire | `createScope({ root })` + `scope.revert()` au cleanup |
| Une seule horloge | `gsap.ticker` pilote Lenis | Aucune autre boucle rAF sauf canvas WebGL |
**Règles runes impératives :**
- État d'animation : `$state` / `$derived` dans le composant ou un module `.svelte.ts`. Jamais de store Svelte legacy pour des valeurs par-frame.
- **Ne jamais écrire des valeurs par-frame (scrollY, pointeur) dans de l'état réactif qui rend du DOM** — écrire dans des variables plain et flusher via rAF.
- GSAP ne doit jamais cibler un nœud que Svelte patche via binding ; cibler via `bind:this` sur du markup statique.
- Tout init GSAP/Anime/Lenis se fait dans `$effect` (ou `onMount`) avec fonction cleanup qui `kill()`/`revert()`/`destroy()`.
---
## 4. BUDGETS PERFORMANCE (mobile 4G, Android entrée de gamme)
| Ressource | Budget |
|---|---|
| JS initial compressé | ≤ 170 KB |
| Textures GPU | ≤ 8 MB, KTX2/BasisU uniquement |
| Modèles 3D | ≤ 500 KB/scène, meshopt ou Draco |
| Média hero avant interaction | ≤ 400 KB |
| Poids total page d'accueil | ≤ 2 MB |
| Animations JS concurrentes | ≤ 8-12 (transform/opacity only) |
| DevicePixelRatio canvas | plafonné à 1.5 |
| Objectif frame rate | 30 fps stables sur Mali-G52, 60 fps desktop |
**SSR vs CSR :** les pages canvas rendent un shell sémantique en SSR et hydratent le canvas côté client. Le prerender statique est le défaut ; l'hébergement cible est un CDN edge (Cloudflare Pages/R2, Netlify) — pas de serveur Node sauf justification explicite.
---
## 5. BIBLIOTHÈQUE DE COMPOSANTS (contrats + implémentations condensées)
### 5.1 SmoothScrollProvider — Lenis au layout racine
```svelte
<script lang="ts">
import { onMount, setContext, type Snippet } from 'svelte';
import Lenis from 'lenis';
import gsap from 'gsap';
let { children }: { children: Snippet } = $props();
let lenis: Lenis | null = $state(null);
setContext('lenis', { get: () => lenis });
onMount(() => {
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
lenis = new Lenis({ lerp: 0.1 });
gsap.ticker.add((t) => lenis?.raf(t * 1000));
gsap.ticker.lagSmoothing(0);
return () => { lenis?.destroy(); lenis = null; };
});
</script>
{@render children()}
```
### 5.2 KineticText — titrage cinétique au scroll
Split par **mots** (jamais par caractères pour les langues à apostrophes), `aria-label` avec la chaîne intacte, scrub via ScrollTrigger ou `animation-timeline: view()` en premier choix, stagger piloté par les tokens de motion du projet.
### 5.3 ParallaxLayer — profondeur multi-plans
CSS-first : si `CSS.supports('animation-timeline: view()')`, animer via keyframes + `view()` ; sinon fallback GSAP ScrollTrigger `scrub: true`, `yPercent` proportionnel à une prop `depth` (01). Toujours `transform`, jamais `top/margin`.
### 5.4 CursorTracker — curseur physique (desktop uniquement)
Garde `matchMedia('(pointer: coarse)')` → ne rien monter sur tactile. Spring (Anime.js `createSpring` ou custom), scale modulé par la **vélocité** du pointeur (pattern Igloo). Exposer position + vélocité via contexte pour alimenter des uniforms WebGL éventuels (curseur-source-de-lumière, pattern Unseen).
### 5.5 PageTransition — transitions de routes
`onNavigate` + View Transitions API en enhancement progressif ; fallback GSAP (out dans la promise d'`onNavigate`, in via `afterNavigate`). Durées lues depuis les custom properties CSS pour que les deux chemins partagent les mêmes tokens.
### 5.6 ScrollProgressBar — barre de progression
`<div>` fixe + `scaleX(progress)`, `role="progressbar"` avec `aria-valuenow`, listener scroll passif, calcul dans rAF.
### 5.7 FlipbookCanvas — séquence d'images scrubée (pattern Apple)
Action Svelte `use:flipbook`. Sélection de qualité via `navigator.connection.effectiveType` + `saveData` : `2g/slow-2g/saveData`**image fixe unique** ; `3g` → jeu réduit ; `4g` → séquence complète. Frames dessinées sur `<canvas>` via rAF, index = fraction de scroll. Préchargement progressif (ne pas bloquer le LCP).
### 5.8 WebGLCanvas — renderer unique (seulement si P3 le justifie)
Un seul `WebGLRenderer` pour toute l'app, fourni via `setContext` avec clé `Symbol`, scènes enfants enregistrées/retirées au cleanup, `setPixelRatio(min(dpr, 1.5))`, `setAnimationLoop` stoppé et `renderer.dispose()` au démontage. Import dynamique de `three`.
---
## 6. ACCESSIBILITÉ & LADDER DE DÉGRADATION
**Gate unique de reduced-motion** au niveau provider : si `prefers-reduced-motion: reduce` → pas de Lenis, pas de scrub, pas de flipbook (poster), contenu statique complet. Ne jamais implémenter la garde composant par composant.
**Ladder (dans l'ordre) :**
1. `saveData` ou `effectiveType` ≤ 3g → images statiques, zéro préchargement de séquence, vidéo non autoplay.
2. `deviceMemory` ≤ 4 ou échec de création de contexte WebGL → mode DOM-effects-only.
3. JS désactivé → le contenu SSR est complet et lisible.
4. Canvas/WebGL toujours `aria-hidden` avec miroir DOM sémantique du contenu.
5. Attribut `lang` correct sur chaque bascule de langue ; focus déplacé sur le `<h1>` après chaque transition de route.
---
## 7. OUTILLAGE
```jsonc
{
"dependencies": {
"lenis": "^1.3",
"gsap": "^3.13",
"animejs": "^4.2"
// "three": uniquement si P3 le justifie — import dynamique obligatoire
},
"devDependencies": {
"@sveltejs/adapter-static": "^3",
"@gltf-transform/cli": "^4",
"vite-imagetools": "^7",
"vite-plugin-pwa": "^1" // shell hors-ligne
}
}
```
HMR/WebGL : init/dispose symétriques dans `$effect`, shaders en imports `?raw` (hot-reload propre), paramètres de debug dans le hash d'URL.
---
## 8. DÉFINITION DE « TERMINÉ » (DoD — tout doit être vrai)
- [ ] Rapport d'audit livré et bugs de production pré-existants corrigés ou explicitement reportés.
- [ ] Aucune ressource chargée depuis un domaine tiers (fonts, CDN, scripts) sauf décision documentée.
- [ ] Lighthouse mobile : Performance ≥ 90, Accessibilité ≥ 95, Best Practices ≥ 95, SEO ≥ 95.
- [ ] Navigation clavier complète + `prefers-reduced-motion` vérifié manuellement.
- [ ] Page fonctionnelle et lisible avec JS désactivé.
- [ ] 404, vide et hors-ligne designés (P5).
- [ ] Toutes les URLs historiques préservées ou redirigées.
- [ ] Build de production vert, zéro warning d'accessibilité Svelte.