Files
annu-kute-ced/doc2sveltekit-transition/playbook-oki-sveltekit.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

41 KiB
Raw Blame History

PLAYBOOK UNIFIÉ — OKI × SVELTEKIT

Document de rĂ©fĂ©rence opĂ©rationnel pour agent de code — marque OKI, mĂ©thode Awwwards-grade, conventions Svelte 5, briques rĂ©utilisables

Nature : fusion de charte-oki-design-system.md, recette-sveltekit-playbook-agent.md, recette-sveltekit-15-sites-awwwards.md, svelte-5_code_writer.md, svelte_core_bestpractices.md et AGENTS.md, enrichie des leçons réelles de 6 missions (juillet 2026) : migration o-k-i.net, refonte atlas-fediverse, finalisation exitchatcontrol, migration gwada-sirius, refonte UX/motion du jeu JWE, réparation et modernisation d'oki-podcast-reader (voir §5, §5b et §5c). Document local, non versionné (.gitignore).

Usage : à fournir à l'agent pour toute demande du type « transforme ce projet web en projet Svelte aux couleurs OKI, style Awwwards » ou « crée un site/PWA dans cette charte ». Compléter avec le brief de mission du §7.

Priorités en cas de conflit : la marque (§1) prime pour couleurs, typo, voix, iconographie · la méthode (§2) prime pour la technique · les budgets et l'accessibilité ne se négocient jamais.


0. TYPES DE MISSION

A. Transformation d'un site existant (HTML statique, WordPress, Eleventy, SPA sans DA) → SvelteKit aux couleurs OKI. Toujours commencer par l'audit (§2.4 phase 1) — le descriptif du site source est toujours Ă  vĂ©rifier contre le dĂ©pĂŽt rĂ©el (la migration o-k-i.net partait d'un brief « HTML statique » alors que le site Ă©tait Eleventy + 38 JSON i18n).

B. Création d'un nouveau site/PWA dans la charte OKI. Commencer au §2.4 phase 2, en copiant les briques du §4.

C. Finalisation — le chantier est dĂ©jĂ  avancĂ© (migration Ă  90 %, WIP non commitĂ©). Le travail est de finir, pas recommencer : builder d'abord, committer l'existant cohĂ©rent, puis combler les manques (typiquement SEO et rĂ©gressions du changement de gĂ©nĂ©rateur).

D. Refonte ciblĂ©e UX/motion — la stack est dĂ©jĂ  SvelteKit. Le travail est chirurgical : remplacer les piĂšces dĂ©faillantes (fond, carte, timeline, layout) et appliquer charte + motion, sans tout réécrire.

Quatre rĂšgles transverses, apprises sur 5 projets :

  1. Contenu éditorial jamais réécrit sans instruction explicite ; les données priment sur les suppositions (ex. KUTE = Castopod dans les JSON, pas « app PHP » comme le brief le supposait).
  2. Toujours vĂ©rifier l'existence de git en premier (atlas-fediverse n'avait AUCUN dĂ©pĂŽt — git init + commit de l'existant avant toute modification, sinon travail irrĂ©versible).
  3. Charte §7 « harmoniser ≠ tout refaire » : si le projet a dĂ©jĂ  un design system de la famille OKI (tokens panafricains, polices accessibilitĂ©), on ne rebrande pas — on applique mĂ©thode et qualitĂ© (exitchatcontrol, gwada-sirius). La charte complĂšte ne s'applique qu'aux projets sans identitĂ© propre (atlas).
  4. VĂ©rifier l'Ă©tat rĂ©el avant de scoper : sur 5 projets, 2 Ă©taient dĂ©jĂ  SvelteKit (atlas, JWE), 1 en migration Ă  90 % (exitchatcontrol), 1 Ă  80 % (gwada-sirius) — une seule vraie migration complĂšte.

1. MARQUE OKI (canonique — prime sur tout le reste)

1.1 Identité en une phrase

Un studio web militant caribĂ©en dont la marque est un drapeau panafricain — noir, or, vert, rouge — posĂ© sur un fond presque noir, en capitales Archivo. Afrofuturiste caribĂ©en : un drapeau, pas une charte corporate.

Trois conséquences non négociables :

  1. ThÚme sombre = identité par défaut ; le clair est un opt-in (html.light-theme) qui assombrit les accents pour WCAG AA.
  2. L'or porte toute l'interaction — seule couleur d'action.
  3. Angles nets partout — aucune forme organique, aucune bulle trùs arrondie.

1.2 Tokens couleurs (mesurés sur le site réel)

:root {
  /* Noyau (thÚme sombre = défaut) */
  --noir-oki:        #0D0D0D;  /* background */
  --noir-profond:    #1A0F1A;  /* surface */
  --blanc-creme:     #FFF8E7;  /* foreground */
  --line:            rgba(255,255,255,0.10); /* filets — jamais de gris plein */
  --or-oki:          #FDB813;  /* accent : SEULE couleur d'action */
  --rouge-oki:       #FF1654;  /* signal, ponctuation — jamais un lien/bouton */
  /* Étendue (parcimonie) */
  --vert-oki:        #00D66C;  /* succĂšs, validation, dons mensuels */
  --turquoise-caraibes: #00CED1;
  --jaune-soleil:    #FFD700;
  --orange-flamme:   #FF6B35;
  --violet-nuit:     #6B2D5C;
  --bleu-ocean:      #0077BE;
  --or-clair:        #FFE066;  /* survol des boutons */
  --gris-sombre:     #2D1B2E;
  /* Sémantique dérivée */
  --muted:           color-mix(in srgb, var(--blanc-creme) 70%, transparent);
  --card-bg:         rgba(255,255,255,0.03);
}
/* Thùme clair — opt-in, accents assombris WCAG AA */
html.light-theme {
  --noir-oki: #FFF8E7;  --blanc-creme: #0D0D0D;
  --noir-profond: #F5F0E8;  --gris-sombre: #E8DDD0;
  --or-oki: #B87A00;  --vert-oki: #006B3D;  --rouge-oki: #A01030;
  --turquoise-caraibes: #006B75;  --bleu-ocean: #004B7F;
  --or-clair: #D99000;  --jaune-soleil: #B87A00;  --orange-flamme: #C85000;
  --line: rgba(0,0,0,0.12);  --card-bg: rgba(0,0,0,0.03);
}

RĂšgles d'usage : texte secondaire = crĂšme Ă  70-85 % (--muted), pas de couleur dĂ©diĂ©e · filets = blanc 10 % · Rouge = signaler · Vert = valider · Or = agir — ne jamais permuter.

1.3 Typographie

--font-display: 'Archivo', 'Arial Black', sans-serif;   /* 600-900 : TITRES + BOUTONS */
--font-body:    'Inter', -apple-system, BlinkMacSystemFont, sans-serif; /* 400-800 */
  • Titres h1-h4 : Archivo, MAJUSCULES, letter-spacing: -0.01em.
  • Boutons : Archivo 700, uppercase, letter-spacing: 0.03em, bordure 2 px.
  • Corps : Inter, line-height: 1.5.
  • Self-hĂ©bergement obligatoire : woff2 dans static/fonts/ + fonts.css, font-display: swap, preload de la display 700. Aucun appel Google Fonts (doctrine souverainetĂ©). Source pratique : @fontsource/archivo + @fontsource/inter (npm), copier les *-latin-*-normal.woff2.

1.4 Formes, espacements, layout

--radius-sm: 3px;  --radius-md: 6px;   /* jamais plus */
--border-card: 1px solid var(--line);
--border-btn:  2px solid var(--or-oki);
--space-1: .75rem; --space-2: 1rem; --space-3: 1.5rem; --space-4: 2rem; --space-5: 4rem;
--container: 1200px;
  • Sections : padding: var(--space-5) 0 · Grilles : repeat(auto-fit, minmax(300px, 1fr)).
  • Carte canonique : fond --card-bg, liserĂ© gauche 4 px d'accent, survol translateY(-2px Ă  -4px) + bordure d'accent.
  • Tag canonique : fond or 8 % + bordure or 1 px, rayon 3 px.
  • Bouton canonique : balayage or ::after scaleX(0→1) origin left au hover, --ease-syncope.

1.5 Flag-bar — signature n°1

Bandeau 6 px, 4 segments francs : noir 0-25 % · or 25-50 % · vert 50-75 % · rouge 75-100 %. ArrĂȘts nets, jamais de dĂ©gradĂ© entre segments.

.flag-bar { height: 6px; background: linear-gradient(to right,
  #0D0D0D 0 25%, var(--or-oki) 25% 50%, var(--vert-oki) 50% 75%, var(--rouge-oki) 75% 100%); }

Placement : bord infĂ©rieur de la nav fixe + haut du footer. Une occurrence pleine largeur visible par Ă©cran max. Pour ponctuer ailleurs (titres de section, loader), utiliser le flag chip : mĂȘme 4 segments, 56 × 6 px (voir §4). En thĂšme clair, le segment noir devient #000 pur.

1.6 Logotype

Monogramme « OKI » : arche or, point vert, accent rouge sur noir. Fichier canonique 512×512 transparent (aussi og:image, apple-touch-icon, maskable PWA). Ne jamais utiliser un logo de partenaire comme logo OKI.

1.7 Motion — les cadences gwoka

--ease-ka:      cubic-bezier(0.22, 1, 0.36, 1);  /* temps fort : attaque franche, longue tenue */
--ease-syncope: cubic-bezier(0.65, 0, 0.35, 1);  /* contretemps : symétrique, urgent */
--dur-tanbou:   120ms;   /* double-croche — micro-interactions, hovers */
--dur-mesure:   480ms;   /* une mesure — entrĂ©es de section */
--dur-phrase:   960ms;   /* deux mesures — transitions de page, moments solennels */
  1. Le stagger de marque est syncopé (3+3+2) : délais [0, 120, 300, 360, 600, 660, 900, 960] ms pour 8 éléments (puis cycles de +1200 ms). Le rythme est une identité, pas un effet.
  2. Uniquement transform + opacity (+ clip-path pour les masques).
  3. Gate unique prefers-reduced-motion au niveau global, contenu statique complet sans JS ni motion.
  4. Durées toujours lues depuis les tokens, jamais hardcodées.

1.8 Iconographie — zĂ©ro emoji en production

Set SVG OKI : viewBox 24×24, stroke 2 px, angles nets, currentColor (or par dĂ©faut), remplissage rĂ©servĂ© aux ≀ 16 px, noms krĂ©yĂČl. LivrĂ© en <symbol> dans un sprite icons.svg + <svg class="icon"><use href="/icons.svg#ID"/></svg>.

Set de rĂ©fĂ©rence (existant dans o-k-i.net) : ka (tambour, icĂŽne maĂźtresse, 404/loader) · lambi (conque — annonces) · zetwal (Ă©toile 4 branches — navigation, instances) · mawon (brisure de chaĂźne — souverainetĂ©) · lakanmou (flamme — engagement, dons) · jaden (pousse — solidaritĂ©, projets) · kannen (canne — patrimoine, institutions) · lanme (vague — international) · glo (poing — luttes, tarifs solidaires) · kle (cadenas ouvert — libertĂ©, open-source) · pawol (bulle angulaire — langues, traduction) · mizik-note (note — musique).

Pattern contenu : si des emojis vivent dans les textes (JSON), utiliser un helper splitLeadingEmoji() qui convertit l'emoji décoratif en picto du sprite sans toucher au texte (implémenté dans o-k-i.net sections/pictos.ts).

1.9 Voix & ton

Vouvoiement direct et empathique, phrases courtes. La cause du visiteur d'abord, l'outil ensuite. À dire : sur mesure · tarifs adaptĂ©s Ă  vos moyens · organisations engagĂ©es · transition numĂ©rique · Ă©cosystĂšme numĂ©rique libre. À Ă©viter : « template gĂ©nĂ©rique », « clĂ© en main », jargon corporate, startup-speak. Piliers : ORGANISATION KA · INTERNATIONALE · Des solutions pour chaque besoin · Le budget ne doit jamais ĂȘtre un frein. Langues : FR par dĂ©faut ; KA (krĂ©yĂČl) en signature et microcopy (404, loader, remerciements — attribut lang="gcf") ; EN en version dĂ©diĂ©e.

1.10 Imagerie

Illustration afrofuturiste caribéenne : couleurs chaudes saturées sur fonds sombres. Jamais de stock corporate, jamais d'illustration startup générique. AVIF/WebP self-hébergés, loading="lazy" hors LCP.

1.11 Écosystùme

  • Nom KA + logiciel en sous-titre (« BOKANTE — Mastodon ») : la transparence est un pilier.
  • Footer fĂ©dĂ©rĂ© sur tous les sous-domaines : [monogramme] Un service libre opĂ©rĂ© par ORGANISATION KA INTERNATIONALE · o-k-i.net + flag-bar 6 px au-dessus.
  • Favicon = monogramme partout. *.o-k-i.net = outils/fĂ©divers ; pawol.nu = projets culturels (motion plus riche autorisĂ©).

2. MÉTHODE TECHNIQUE (la recette)

2.1 Les 5 principes

  • P1 — Le scroll est le moteur. Une seule instance Lenis au layout racine, cadencĂ©e par le ticker GSAP unique. Jamais de double rAF.
  • P2 — Bake au build, pas au runtime. Tout ce qui ne dĂ©pend pas d'une entrĂ©e live est prĂ©calculĂ© : images responsive (vite-imagetools), textures, polices.
  • P3 — DOM d'abord, WebGL seulement oĂč ça paie. Zentry a fait du « 3D » primĂ© en pur DOM (clip-path, preserve-3d, masks). Pour l'audience OKI (mobile 4G, Mali-G52) : DOM/SVG uniquement par dĂ©faut.
  • P4 — Le motion se designe avant les pages (tokens §1.7 d'abord, composants ensuite).
  • P5 — Chaque Ă©tat est un moment designĂ© : loader, 404, vide, hors-ligne — en KA avec le tambour ka.

2.2 Interdictions absolues

  • Jamais de dĂ©pendance installĂ©e sans ĂȘtre importĂ©e et utilisĂ©e (les outils d'audit — lighthouse, puppeteer — s'installent en --no-save ou se retirent).
  • Jamais de SSR dĂ©sactivĂ© globalement pour « faire marcher » une lib client.
  • Jamais animer autre chose que transform/opacity/clip-path en JS.
  • Jamais de page dont le contenu est invisible sans JavaScript.
  • Jamais de service tiers (fonts, CDN, analytics) — tout asset se self-hĂ©berge.

2.3 Stack cible

SvelteKit 2 + Svelte 5 (runes) + TypeScript strict
@sveltejs/adapter-static (prerender intégral, trailingSlash 'always')
vite-imagetools (AVIF/WebP responsive au build)
vite-plugin-pwa (generateSW — shell hors-ligne)
lenis + gsap (imports dynamiques uniquement)
@fontsource/* (polices woff2 copiées dans static/fonts/)
CSS vanilla : oki-tokens.css + base.css + styles scopĂ©s — pas de Tailwind

2.4 Workflow en 7 phases

  1. Audit (toujours) : pages, sections, assets, liens externes, stack mesurée, bugs de production listés AVANT toute refonte, contradictions doctrine/outillage (ex. discours anti-GAFAM + Google Fonts). Rapport validé avant de coder.
  2. Tokens & thÚme : oki-tokens.css (§1), thÚme sombre défaut + clair opt-in, fonts self-hébergées, anti-FOUC par script externe (CSP).
  3. Architecture : routes, i18n, SEO, layout. prerender = true partout. URLs historiques conservées.
  4. Composants : uniquement ceux pertinents pour CE site. Chacun avec cleanup et garde reduced-motion.
  5. Motion : transitions Svelte natives · scrub via animation-timeline: view()/scroll() natif d'abord, fallback GSAP ScrollTrigger en import dynamique · une seule horloge (gsap.ticker → Lenis).
  6. Performance : budgets §2.7, pipeline images, mesure sur profil mobile.
  7. Accessibilité & dégradation : ladder §2.8, revue clavier, contenu canvas/SVG doublé en DOM.

2.5 Architecture de référence (éprouvée sur o-k-i.net)

src/
  app.html                 # %lang% + scripts externes (theme, lang-redirect, registerSW)
  hooks.server.ts          # lang fr/en via transformPageChunk (replaceAll '%lang%')
  app.d.ts / imagetools.d.ts
  lib/
    styles/oki-tokens.css  # charte §1 — LE fichier de tokens partagĂ©
    styles/base.css        # reset, primitives (btn, card, tag, flag-bar, icon), gate reduced-motion
    i18n/                  # bundles JSON par locale + index.ts (getBundle, localeFromPath, alternatePath)
    assets/images/         # sources pour vite-imagetools
    motion/                # tokens.ts, scroll.ts (Lenis), reveal.ts, tilt.ts
    components/            # Seo, Nav, Footer, ResponsiveImage, motion/, icons/, sections/
  routes/
    +layout.svelte         # skip-link, loader, progress, nav, footer, View Transitions, Lenis
    +layout.ts             # prerender = true, trailingSlash = 'always'
    +error.svelte          # 404/erreurs designée (KA)
    offline/+page.svelte   # cible navigateFallback du SW
static/
  fonts/ icons.svg images/ theme.js lang-redirect.js registerSW.js
  manifest.webmanifest 404.html robots.txt sitemap.xml _headers .htaccess

2.6 Composants motion (contrats)

  • KineticText — titrage au scroll, split par mots (jamais caractĂšres — apostrophes), aria-label sur titres h1-h4 (jamais sur span : prohibĂ©), scrub animation-timeline: view() + fallback ScrollTrigger dynamique, stagger syncopĂ© converti en plages de scroll.
  • ScrollProgressBar — scaleX via rAF + Ă©criture DOM directe (jamais d'Ă©tat rĂ©actif par frame), role="progressbar".
  • PageTransition — onNavigate + View Transitions API, durĂ©e lue des tokens ; fallback sobre = navigation instantanĂ©e.
  • IntroLoader — cĂ©rĂ©monie 1re visite (sessionStorage), skippable, 100 % CSS pilotĂ©e par une classe posĂ©e par le script externe anti-FOUC (pas de JS inline — CSP).
  • Marquee — bande typographique CSS pure, contenu dupliquĂ© aria-hidden, pause au hover, coupĂ©e en reduced-motion.
  • use:reveal — action IO, cascade syncopĂ©e via --d, Ă©tat cachĂ© uniquement sous html.js + prefers-reduced-motion: no-preference.
  • use:tilt — tilt 3D pointeur via variables CSS --rx/--ry + rAF, off tactile + reduced-motion.
  • FlagChip — mini flag-bar 56×6 px qui se dessine (scaleX) Ă  l'entrĂ©e du titre.
  • Village Ă©cosystĂšme — scĂšne SVG isomĂ©trique des services, chaque bĂątiment = <a> focusable (aria-label « NOM — Logiciel »), hover/focus = liserĂ© or + label, fallback grille accessible (reduced-motion + mobile Ă©troit, bascule display: none pour Ă©viter les doubles tab stops).
  • CursorTracker, WebGL : non retenus pour l'audience OKI (mobile-dominante).

2.7 Budgets (mobile 4G, Android entrée de gamme)

Ressource Budget Mesuré o-k-i.net
JS initial compressĂ© ≀ 170 Ko 69 Ko gzip (+45 Ko dynamiques)
Poids total accueil ≀ 2 Mo ~0,9 Mo
MĂ©dia hero avant interaction ≀ 400 Ko ~12 Ko (logo)
Lighthouse mobile ≄ 90/95/95/95 91/100/100/100
Animations JS concurrentes ≀ 8-12, transform/opacity OK
LCP / INP / CLS < 2,5 s / < 200 ms / < 0,05 2,9 s / 59 ms TBT / 0,001

2.8 Accessibilité & ladder de dégradation

  1. saveData/effectiveType ≀ 3g → images statiques, zĂ©ro prĂ©chargement.
  2. JS dĂ©sactivĂ© → contenu SSR complet et lisible (Ă©tats cachĂ©s conditionnĂ©s Ă  html.js).
  3. prefers-reduced-motion → gate unique : pas de Lenis, pas de loader, pas de scrub, contenu statique complet.
  4. Canvas/SVG décoratifs aria-hidden + miroir DOM sémantique.
  5. lang correct à chaque bascule ; focus déplacé sur le contenu aprÚs transition de route ; skip-link ; focus visible or.
  6. PiÚges éprouvés : aria-label interdit sur <span> générique · <figcaption> enfant direct de <figure> · liens dans le texte soulignés (scoper la rÚgle à main pour ne pas toucher nav/footer).

2.9 PWA légÚre

  • static/manifest.webmanifest manuel (nom KA, monogramme any + maskable, theme_color/background_color --noir-oki, display: standalone).
  • vite-plugin-pwa : registerType: 'autoUpdate', injectRegister: false, manifest: false, workbox navigateFallback: '/offline/index.html', navigateFallbackDenylist pour les assets binaires, globIgnores pour les gros fichiers (PDF), maximumFileSizeToCacheInBytes: 3 Mo, cleanupOutdatedCaches, clientsClaim, skipWaiting.
  • Enregistrement par fichier statique static/registerSW.js avec chemins absolus (navigator.serviceWorker.register('/sw.js', { scope: '/' })), liĂ© dans app.html. Ne PAS utiliser l'injection du plugin : sans @vite-pwa/sveltekit elle n'atteint pas le HTML prĂ©-rendu, et le registerSW.js gĂ©nĂ©rĂ© utilise un chemin relatif ./sw.js cassĂ© sur les routes imbriquĂ©es (/en/, /dons/).

2.10 Sécurité & CSP

CSP de référence (tout self-hébergé) :

default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';
img-src 'self' data:; font-src 'self'; connect-src 'self';
frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
  • HSTS max-age=31536000, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy restrictive. LivrĂ©e en deux formats : static/.htaccess (Apache/o2switch : + ErrorDocument 404 /404.html, redirect HTTPS, cache immutable /_app/ et /fonts/) et static/_headers (Cloudflare Pages). RĂšgles : aucun script inline (snippets thĂšme/langue en fichiers externes) ; style attributes OK (unsafe-inline style) ; 404 statique autonome (static/404.html, zĂ©ro dĂ©pendance).

2.11 SEO

Composant Seo.svelte : title/description par page, canonical, hreflang fr/en/x-default via mapping de paires d'URLs, OG ×10 (og:image self-hĂ©bergĂ©e), Twitter Card, JSON-LD (Organization + entitĂ©s du projet). robots.txt + sitemap.xml statiques Ă  jour. Astuce : les </script> dans les template literals {@html} doivent ĂȘtre Ă©chappĂ©s (<\/script>).


3. CONVENTIONS SVELTE 5 (obligatoires)

  • Runes only : $state (uniquement pour du rĂ©actif — sinon variable plain ; gros objets rĂ©assignĂ©s : $state.raw), $derived (jamais d'$effect pour calculer), $props (traiter les props comme changeantes : valeurs dĂ©pendantes en $derived).
  • ÉvĂ©nements : onclick={...} (jamais on:click) ; listeners window/document via <svelte:window> / <svelte:document>.
  • {#each} Ă  clĂ© unique (jamais l'index).
  • CSS : variables JS → directive style:--var={val} ; styles enfants via custom properties, :global en dernier recours ; Ă©tats globaux (html.js, html.light-theme, html.intro-pending) via :global(...).
  • États rĂ©actifs par-frame interdits : scrollY/pointeur → variables plain + flush rAF (ou variables CSS directement).
  • Init/cleanup : GSAP/Lenis/IO dans onMount/$effect avec cleanup symĂ©trique (kill()/revert()/destroy()/disconnect()).
  • Liens internes : resolve() / assets via asset() de $app/paths ; externes en rel="external noopener noreferrer" + target="_blank".
  • {@html} : uniquement sur contenu first-party (JSON du dĂ©pĂŽt), commentĂ© comme tel.
  • Autofixer (AGENTS.md) : aprĂšs tout composant modifiĂ© —
    npx @sveltejs/mcp list-sections                      # doc Svelte 5
    npx @sveltejs/mcp get-documentation "$state,$derived"
    npx @sveltejs/mcp svelte-autofixer ./src/lib/MonComposant.svelte
    
    (Échapper \$ dans le code inline. La sortie est un objet JS, pas du JSON strict.) npm run check à 0 erreur / 0 warning avant tout commit.

4. BRIQUES RÉUTILISABLES (rĂ©fĂ©rence : dĂ©pĂŽt o-k-i.net, branche svelte)

Copier et adapter, ne pas réécrire. Chaque brique a un contrat stable.

Brique Chemin dans o-k-i.net Contrat / usage
Tokens charte src/lib/styles/oki-tokens.css importé en premier dans +layout.svelte
Primitives src/lib/styles/base.css .btn .card .tag .icon .flag-bar .container .section, gate reduced-motion, reveal
Fonts static/fonts/ + fonts.css Archivo 600-900, Inter 400-800, woff2 latin, preload 700
Anti-FOUC thĂšme static/theme.js pose .js, .light-theme, .intro-pending ; localStorage oki-theme
Redirection langue static/lang-redirect.js préférence oki-lang-pref, sinon navigator.language
i18n src/lib/i18n/index.ts bundles JSON par locale ; getBundle(locale), localeFromPath, alternatePath (paires FR↔EN)
SEO src/lib/components/Seo.svelte <Seo title? description? path locale /> — meta + OG + Twitter + JSON-LD
Lang par route src/hooks.server.ts %lang% dans app.html + transformPageChunk
Lenis src/lib/motion/scroll.ts initSmoothScroll() (dynamic), scrollToAnchor(hash)
Tokens motion src/lib/motion/tokens.ts prefersReducedMotion(), syncopatedDelay(i) (3+3+2)
Reveal src/lib/motion/reveal.ts use:reveal={index} — cascade gwoka
Tilt src/lib/motion/tilt.ts use:tilt — CSS --rx/--ry
KineticText components/motion/KineticText.svelte <KineticText text as="h2" class="section-title" />
ScrollProgressBar components/motion/ScrollProgressBar.svelte global, gradient or→vert
IntroLoader components/motion/IntroLoader.svelte cérémonie 1re visite, skippable
Marquee components/motion/Marquee.svelte <Marquee {t} />
FlagChip components/motion/FlagChip.svelte ornement de titre, dessin au scroll
Images components/ResponsiveImage.svelte + imagetools.d.ts import pic from './x.png?format=avif;webp;png&w=
&as=picture' → <ResponsiveImage picture={pic} alt />. Le wildcard declare module '*&as=picture' doit vivre dans un .d.ts NON-module
Sprite OKI static/icons.svg 12 pictos KA (§1.8) — <use href="/icons.svg#ID">
Logos marques components/icons/BrandIcon.svelte `<BrandIcon name="mastodon
Village components/sections/HostingVillage.svelte scÚne SVG écosystÚme + fallback grille
Nav/Footer components/Nav.svelte, Footer.svelte nav active (IO), dropdown accessible, thÚme, langue ; footer fédéré
Emojis→pictos components/sections/pictos.ts splitLeadingEmoji(str), pictoFor(emoji)
Bridge tokens atlas-fediverse/src/styles/oki-bridge.css alias des anciens noms de tokens vers la charte — applique la charte à un projet existant sans retoucher chaque composant
Layout bakĂ© atlas-fediverse/scripts/build-layout.mjs d3-force en script node → positions dĂ©terministes figĂ©es en JSON (init hashĂ©e FNV-1a + ticks fixes = bit-identique). Rejouer quand les donnĂ©es changent
État partagĂ© inter-sections atlas-fediverse/src/lib/software-modal.svelte.ts module $state + requestX()/consumeX() : ouvrir une modale d'un composant depuis une autre section (timeline → catalogue)
Layout app 100dvh JWE/app/src/lib/styles/jeu.css height: 100dvh (pas min-height) + overflow par panneau + overlays consolidĂ©s (une puce de statut, rangĂ©es de boutons hors de la carte, barre d'actions opaque z-indexĂ©e) — zĂ©ro scroll, zĂ©ro superposition
Count-up score JWE/app/src/lib/motion/countup.ts use:compte={cible} : rAF → Ă©criture DOM directe, span animĂ© aria-hidden + valeur finale en .sr-only (rĂ©gion aria-live)
Reveal SSR JWE/app/src/lib/motion/reveal.ts variante dont l'état initial est rendu cÎté serveur, masqué uniquement sous html.js + no-preference
i18n dossiers gwada-sirius/src/lib/i18n/index.ts catalogues au format Paraglide/inlang consommĂ©s par un routeur maison (pas de middleware) — statique-friendly, l'outillage inlang reste utilisable
Support volume iOS svelte-podcast/src/lib/volume-support.ts can_set_volume() : sonde l'Ă©criture de volume sur un Ă©lĂ©ment jetable (mĂ©moĂŻsĂ©e) — dĂ©tecte iOS par capacitĂ©, pas par UA sniffing
PWA vite.config.ts (bloc VitePWA) + static/registerSW.js + static/manifest.webmanifest voir §2.9
Headers static/.htaccess + static/_headers voir §2.10
404 / offline static/404.html + routes/+error.svelte + routes/offline/ KA + tambour ka, autonomes

5. LEÇONS DE LA MIGRATION o-k-i.net (piĂšges dĂ©jĂ  rĂ©solus — ne pas les rĂ©-apprendre)

  1. Toujours auditer le dĂ©pĂŽt rĂ©el : le brief disait « HTML statique », c'Ă©tait Eleventy + i18n JSON. Le contenu vivait dans les donnĂ©es — les reprendre, pas réécrire.
  2. Les données priment sur le brief : le village suit services.json (4 instances), pas les exemples du brief ; la section cible se décide d'aprÚs le contenu JSON, pas le titre supposé.
  3. vite preview meurt si l'on rebuilde pendant qu'il tourne (il sert .svelte-kit/output) — redĂ©marrer aprĂšs chaque build.
  4. CSP script-src 'self' : anti-FOUC, lang-redirect et registerSW en fichiers externes statiques ; aucun inline.
  5. Preview headless : --virtual-time-budget fige les animations d'entrĂ©e (faux nĂ©gatifs visuels) — valider le motion en temps rĂ©el via puppeteer (--no-save), pas en screenshot one-shot.
  6. Puppeteer QA : page.emulateMediaFeatures pour reduced-motion ; le tilt se teste par dispatchEvent(PointerEvent) (le scroll Lenis décale mouse.move).
  7. eslint/autofixer no-navigation-without-resolve : resolve() pour routes internes, asset() pour fichiers statiques, rel="external" pour sortir du scope de la rùgle — sans jamais envelopper une URL externe ou une ancre #.
  8. Grep de vĂ©ritĂ© avant de livrer : zĂ©ro domaine tiers dans build/ (hors liens <a> mĂ©tier), zĂ©ro emoji dans le HTML buildĂ©, toutes les URLs en 200, og: ×10, JSON-LD prĂ©sents.
  9. Lighthouse aprĂšs chaque passe (mobile) et corriger ce qui est remontĂ© — les deux points gagnĂ©s sur o-k-i.net : aria-label sur span (KineticText) et liens non soulignĂ©s.
  10. Lighthouse link-in-text-block : scoper text-decoration: underline Ă  main — sinon la nav et le footer hĂ©ritent de soulignĂ©s partout.

5b. LEÇONS DES MISSIONS atlas / exitchatcontrol / gwada-sirius / JWE

Gouvernance & process

  1. git d'abord : atlas n'avait aucun dĂ©pĂŽt — git init + commit de l'existant avant toute ligne de code.
  2. WIP non commité d'autrui : builder d'abord ; si c'est cohérent, committer tel quel, puis améliorer par commits séparés (exitchatcontrol).
  3. Orchestration d'agents parallĂšles : sĂ©rialiser tout ce qui touche package.json / npm (un seul agent ou le parent) ; dĂ©finir les contrats (props des composants, noms de tokens, chemins) AVANT de lancer — les agents s'intĂšgrent alors sans conflit, et dĂ©couvrent mĂȘme le travail des autres (module software-modal rĂ©utilisĂ© en vol).
  4. Conflits de ports : avant tout audit automatisĂ©, vĂ©rifier ce qui tourne (vite preview d'un autre projet sur le mĂȘme port a faussĂ© un audit axe — vĂ©rifier le <title> servi).

Technique SvelteKit 5. paths.relative: false quand paths.base est utilisĂ© : par dĂ©faut $app/paths.base vaut ".." au prerender, ce qui casse toutes les comparaisons de pathname (startsWith(base), switcher de langue, aria-current) et produit des hrefs relatifs bizarres dans le HTML prĂ©rendu. L'hydratation masque le bug — le vĂ©rifier dans le HTML buildĂ©, pas seulement au clic. 6. lang par route avec base path : retirer base dans hooks.server.ts AVANT de dĂ©duire la locale du premier segment. 7. i18n sans middleware : un routeur par dossiers (/, /en/, /ht/) + loader JSON maison bat Paraglide-officiel pour le statique (URLs et prerender sous contrĂŽle total) tout en gardant les catalogues au format inlang. 8. Îlots → composants natifs : le montage manuel (mount.js + IntersectionObserver) disparaĂźt ; le fallback SSR (table, liste) devient le markup mĂȘme du composant — meilleur pour no-JS ET pour le CLS. 9. Leaflet sous SSR : await import('leaflet') dans onMount (l'import statique plante cĂŽtĂ© Node), CSS en import statique OK, L.divIcon CSS au lieu d'images de marqueurs. Tuiles OSM = exception documentĂ©e au zĂ©ro-tiers, Ă  faire trancher par le propriĂ©taire. 10. CSP d'un jeu/app riche (adapter-node → hooks.server.ts) : Ă©numĂ©rer les domaines rĂ©els en inspectant le code ET les node_modules (Mapillary a besoin de graph. + tiles. + images.mapillary.com, Wikimedia d'upload. + commons.) — puis jouer une manche complĂšte en capturant la console : zĂ©ro violation exigĂ©e. 11. Layout « app » sans scroll : height: 100dvh (jamais min-height) + overflow gĂ©rĂ© par panneau + indices dans des panneaux scrollables internes. Les bugs de superposition mobile viennent d'overlays absolus empilĂ©s : consolider les badges en une puce, sortir les rangĂ©es de boutons de la carte, barre d'actions opaque avec z-index explicite, pointer-events: none sur tout conteneur dĂ©coratif. Tester chaque bouton par elementFromPoint. 12. Le « moment de rĂ©vĂ©lation » (jeux) : ligne de distance dessinĂ©e + score en count-up + marqueur qui tombe. Count-up : rAF → DOM direct, jamais d'Ă©tat rĂ©actif par frame ; span animĂ© aria-hidden + valeur finale .sr-only dans une rĂ©gion aria-live. 13. CSP hashĂ©e (script postbuild qui hashe les inline) : un JSON-LD inline ajoutĂ© est hashĂ© automatiquement — mais vĂ©rifier le compte d'empreintes aprĂšs build. 14. IcĂŽnes PNG lourdes : PIL.Image.quantize(256) suffit pour des logos (121 Ko → 9 Ko, perte invisible) — toujours relire l'image aprĂšs.

Contenu & donnĂ©es 15. Audit factuel des donnĂ©es en plus de l'audit technique : la timeline d'atlas contenait 4 dates fausses et un compteur pĂ©rimĂ© (Twitter 2021→2022, Mastodon 2015→2016, PeerTube 2015→2017, Bluesky 2023→2024, « 55+ »→105). Les champs de donnĂ©es inutilisĂ©s (month, softwareId) sont souvent des fonctionnalitĂ©s gratuites. 16. RĂ©gressions SEO typiques au changement de gĂ©nĂ©rateur : sitemap, OG hors fiches, og:image, canonical — checklist Ă  passer systĂ©matiquement (exitchatcontrol).

Déploiement YunoHost 17. Le package ne consomme que des archives de tag : taguer l'app (vX.Y.Z), pousser le tag, télécharger l'archive /archive/<tag>.tar.gz depuis la forge, recalculer son sha256, mettre à jour manifest.toml (version X.Y.Z~ynhN + url + sha256), commit/push le package. autoupdate.strategy = "latest_forgejo_tag" détecte les tags suivants.

5c. LEÇONS DE LA MISSION oki-podcast-reader (lecteur audio)

Gouvernance & forks

  1. SĂ©curiser le WIP AVANT tout : toute la transformation applicative Ă©tait non commitĂ©e sur un fork — premier commit de protection avant la premiĂšre analyse dĂ©taillĂ©e (rĂšgle gĂ©nĂ©rale : git add -A && git commit "WIP sĂ©curisĂ©" dĂšs qu'un working tree contient du travail non versionnĂ©).
  2. Fork de librairie → application : purger la dette de packaging (champs exports/files/peerDependencies, changesets, publint, workflows CI de la lib) — le package.json d'une app est private: true et nu.
  3. Le remote origin d'un fork pointe chez l'auteur original : le renommer upstream avant d'ajouter le remote de déploiement, sinon le push part chez l'original (échoue ou pire).

Upgrades de framework 4. Le mode compat Svelte 5 est une voie complĂšte : SvelteKit 1→2 + Svelte 4→5 SANS réécriture en runes — la syntaxe legacy (on:click, $:, export let, <slot>) compile Ă  0 erreur/0 warning. Upgrader les paquets, traiter uniquement les breaking changes (imports vitePreprocess, options TS supprimĂ©es, sĂ©rialisation), ne JAMAIS réécrire la syntaxe dans la mĂȘme passe. La réécriture en runes est une mission sĂ©parĂ©e, facultative. 5. Respecter le gestionnaire de paquets du projet (yarn.lock ≠ package-lock.json) — ne pas mĂ©langer.

RĂšgle d'architecture des loads (la plus gĂ©nĂ©ralisable) 6. Universal load (+page.ts) = le corps brut des fetch est inlinĂ© dans le HTML : un flux RSS de 10 Mo produisait une page de 11,9 Mo. Server load (+page.server.ts) garde le fetch cĂŽtĂ© serveur — rĂšgle d'un coup : bloat supprimĂ© (−88 %), ET les problĂšmes CORS en navigation client disparus (le navigateur ne refait jamais le fetch : il lit __data.json). Toute donnĂ©e tierce fetchĂ©e appartient Ă  +page.server.ts. Corollaire : les pages qui en dĂ©pendent ne buildent pas hors-ligne — Ă  documenter. 7. Les redirections CORS se contrĂŽlent Ă  chaque saut : une 302 sans Access-Control-Allow-Origin tue le fetch navigateur mĂȘme si la destination finale l'autorise — raison de plus pour fetcher cĂŽtĂ© serveur.

Audio / mĂ©dia 8. Les prĂ©fĂ©rences mĂ©dia persistĂ©es doivent ĂȘtre re-appliquĂ©es en continu (souscription), pas seulement au chargement de la source — et ?? 1, jamais || 1 (un 0 persistĂ© est une valeur lĂ©gitime). 9. HTMLMediaElement.volume est en lecture seule sur iOS : dĂ©tecter par capacitĂ© (sonder l'Ă©criture sur un Ă©lĂ©ment jetable, mĂ©moĂŻser), masquer le slider, garder le mute (el.muted, lui, fonctionne). Source de vĂ©ritĂ© unique dans le store persistĂ©, synchronisĂ©e dans les deux sens. 10. Cibles tactiles sur les lecteurs : Ă©paisseur visuelle ≠ zone de hit (piste fine de 10 px OK si la zone fait 44 px). Un <input type="range"> ne repositionne PAS le thumb au tap tactile — handlers touchstart/touchmove explicites requis (tester avec page.tap + hasTouch). 11. Media Session API est non nĂ©gociable pour une app audio (contrĂŽles Ă©cran verrouillĂ©) : metadata Ă  chaque piste + handlers play/pause/previoustrack/nexttrack, gardĂ© par 'mediaSession' in navigator. 12. SW d'une app audio : ne JAMAIS intercepter les streams (requĂȘtes Range, extensions mĂ©dia) — prĂ©cache shell, cache-first sur pochettes uniquement.

Perf de listes 13. content-visibility: auto + contain-intrinsic-size : le gain de perf de listes le moins cher qui existe (CSS seul). Avant toute virtualisation : pagination « charger plus » 50×50 (1022 lignes DOM → 50).


6. PATTERNS AWWWARDS (distillĂ© des 15 sites — quoi voler, Ă  quel coĂ»t)

Pattern (source) Coût Statut pour OKI
DOM-only masks/clip-path « 3D » (Zentry) 5 % du coût GPU Adopté (wipes pochettes)
Motion system spec'd avant les pages (Zentry, P4) 0 Adopté (tokens gwoka)
Hover craft budgĂ©tĂ© — 5 interactions intentionnelles (Noomo) faible AdoptĂ© (balayage boutons, tilt, soulignĂ©s, village)
Loader-as-fiction (KPR) faible Adopté (IntroLoader)
États designĂ©s : 404, vide, offline (Studio375) faible AdoptĂ© (KA + tambour)
Dollhouse IA — sections = lieux physiques (Kriss.ai) moyen AdoptĂ© (village crĂ©ole)
Constellation zetwal (charte §7 — prescription portails fĂ©divers) faible AdoptĂ© (hero atlas, remplace un shader Three.js de 708 Ko)
Layout bakĂ© au build — positions de graphe figĂ©es (dĂ©clinaison P2) faible AdoptĂ© (carte atlas : SVG 2D dĂ©terministe, −750 Ko de JS)
Moment de rĂ©vĂ©lation designĂ© — ligne de distance + count-up + chute de marqueur (jeux) faible AdoptĂ© (JWE)
Bilingual kinetic type — la bascule de langue comme Ă©vĂ©nement (Nudot) faible Candidat (Ă©volution du lang-switch FR/KA)
Chaptered scroll-comic (ten.375.studio) moyen Candidat (lore DJANGOKAM, patrimoine)
Scroll flipbook connection-aware (Apple) moyen Candidat si séquence visuelle un jour (gating effectiveType obligatoire)
Bake noise/textures offline (David Whyte) faible Candidat (fonds génératifs bakés)
Backstage / process public (Immersive Garden) faible Candidat (contenu pédagogique = souveraineté)
Cursor-as-light, velocity particles (Unseen, Igloo) élevé Rejeté (audience tactile)
WebGL full-UI (Igloo), skeletal scroll camera (HAPE), flipbook géant trÚs élevé Rejeté (budgets 4G/Mali-G52)
Resolution gating (KPR) — Interdit (hostile mobile)

7. TEMPLATE DE BRIEF DE MISSION (Ă  remplir par projet)

# MISSION : [Transformer X / CrĂ©er Y] selon le PLAYBOOK UNIFIÉ OKI × SVELTEKIT

## 1. Contexte
- Site/projet : [URL/dĂ©pĂŽt] — stack actuelle mesurĂ©e : [Ă  auditer, §2.4 phase 1]
- Identité : [association/projet, positionnement]
- ÉcosystĂšme liĂ© (liens externes Ă  prĂ©server) : [liste]
- Bugs de production confirmés : [liste audit]

## 2. Contenu éditorial
- [Reprendre à l'identique / réécriture autorisée : périmÚtre]
- Langues : [FR / EN / KA — URLs]

## 3. Exigences techniques
- Stack : §2.3 du playbook (dĂ©faut) — Ă©carts Ă©ventuels : [liste]
- URLs Ă  conserver : [liste + ancres]
- Hébergement cible : [o2switch statique / Cloudflare Pages]
- PWA : [oui/non]

## 4. Motion & composants (sĂ©lection depuis §2.6 — rester sobre)
- [ ] KineticText titres  [ ] IntroLoader  [ ] Marquee  [ ] Village/scĂšne
- [ ] Autre : [prĂ©ciser]  — WebGL : [non par dĂ©faut]

## 5. PérimÚtre de cette session
- [ ] 


## 6. CritĂšres d'acceptation
- DoD §8 intégralement vérifiée + [spécifiques projet]

8. DOD — DÉFINITION DE « TERMINÉ » (tout doit ĂȘtre vrai)

Fondations

  • Rapport d'audit livrĂ© ; bugs prĂ©-existants corrigĂ©s ou explicitement reportĂ©s · git vĂ©rifiĂ©/initialisĂ© avant tout travail.
  • Tokens charte importĂ©s (fichier partagĂ©, pas de valeurs recopiĂ©es) ; thĂšme sombre dĂ©faut, clair opt-in AA.
  • Fonts self-hĂ©bergĂ©es ; zĂ©ro requĂȘte tierce au chargement (vĂ©rifiĂ© onglet rĂ©seau + grep du build).
  • npm run check : 0 erreur, 0 warning · npm run build vert · svelte-autofixer propre sur les fichiers modifiĂ©s.
  • Si paths.base : paths.relative: false et hrefs du HTML prĂ©rendu inspectĂ©s (pas de ../ ni de double prĂ©fixe).
  • Audit factuel des donnĂ©es (dates, compteurs, champs inutilisĂ©s) en plus de l'audit technique.
  • Toute donnĂ©e tierce fetchĂ©e passe par +page.server.ts (un universal load inline le corps brut des fetch dans le HTML).

Marque

  • Or = seule couleur d'action ; rouge/vert dans leurs rĂŽles · angles nets (≀ 6 px).
  • Flag-bar segments francs, ≀ 1 pleine largeur par Ă©cran · zĂ©ro emoji en interface (sprite SVG).
  • Tokens motion gwoka + stagger syncopĂ© · gate prefers-reduced-motion vĂ©rifiĂ©e manuellement.
  • Voix conforme (test : aucune occurrence de « clĂ© en main », « solution innovante », « disruptive »).

Expérience & accessibilité

  • Navigation clavier complĂšte (tab order, focus visible, skip-link, village/scĂšne/carte focusable).
  • ZĂ©ro superposition : chaque bouton testĂ© par elementFromPoint (mobile 390 px) ; pointer-events: none sur tout conteneur dĂ©coratif.
  • Layout app : height: 100dvh si « tout visible sans scroll » est exigĂ© — scrollHeight === innerHeight mesurĂ© mobile ET desktop.
  • Page fonctionnelle et lisible avec JS dĂ©sactivĂ©.
  • 404, vide et hors-ligne designĂ©s (KA + tambour) · loader designĂ© si prĂ©sent, skippable.
  • lang correct par locale · hreflang · OG complet self-hĂ©bergĂ© · JSON-LD.

Mesures

  • Lighthouse mobile : Performance ≄ 90, AccessibilitĂ© ≄ 95, Best Practices ≄ 95, SEO ≄ 95.
  • JS initial ≀ 170 Ko gzip · accueil ≀ 2 Mo · images optimisĂ©es (aucune > 200 Ko hors hero, lazy hors LCP).
  • Toutes les URLs historiques prĂ©servĂ©es ou redirigĂ©es · sitemap/robots Ă  jour.