- 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
41 KiB
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.mdetAGENTS.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 :
- 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).
- 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). - 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).
- 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 :
- ThÚme sombre = identité par défaut ; le clair est un opt-in (
html.light-theme) qui assombrit les accents pour WCAG AA. - L'or porte toute l'interaction â seule couleur d'action.
- 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, survoltranslateY(-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 */
- 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. - Uniquement
transform+opacity(+clip-pathpour les masques). - Gate unique
prefers-reduced-motionau niveau global, contenu statique complet sans JS ni motion. - 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-saveou se retirent). - Jamais de SSR désactivé globalement pour « faire marcher » une lib client.
- Jamais animer autre chose que
transform/opacity/clip-pathen 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
- 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.
- 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). - Architecture : routes, i18n, SEO, layout.
prerender = truepartout. URLs historiques conservées. - Composants : uniquement ceux pertinents pour CE site. Chacun avec cleanup et garde reduced-motion.
- 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). - Performance : budgets §2.7, pipeline images, mesure sur profil mobile.
- 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-labelsur titres h1-h4 (jamais sur span : prohibĂ©), scrubanimation-timeline: view()+ fallback ScrollTrigger dynamique, stagger syncopĂ© converti en plages de scroll. - ScrollProgressBar â
scaleXvia 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 soushtml.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, basculedisplay: nonepour Ă©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
saveData/effectiveType†3g â images statiques, zĂ©ro prĂ©chargement.- JS dĂ©sactivĂ© â contenu SSR complet et lisible (Ă©tats cachĂ©s conditionnĂ©s Ă
html.js). prefers-reduced-motionâ gate unique : pas de Lenis, pas de loader, pas de scrub, contenu statique complet.- Canvas/SVG dĂ©coratifs
aria-hidden+ miroir DOM sémantique. langcorrect à chaque bascule ; focus déplacé sur le contenu aprÚs transition de route ; skip-link ; focus visible or.- PiÚges éprouvés :
aria-labelinterdit sur<span>gĂ©nĂ©rique ·<figcaption>enfant direct de<figure>· liens dans le texte soulignĂ©s (scoper la rĂšgle Ămainpour ne pas toucher nav/footer).
2.9 PWA légÚre
static/manifest.webmanifestmanuel (nom KA, monogramme any + maskable,theme_color/background_color--noir-oki,display: standalone).vite-plugin-pwa:registerType: 'autoUpdate',injectRegister: false,manifest: false, workboxnavigateFallback: '/offline/index.html',navigateFallbackDenylistpour les assets binaires,globIgnorespour les gros fichiers (PDF),maximumFileSizeToCacheInBytes: 3 Mo,cleanupOutdatedCaches,clientsClaim,skipWaiting.- Enregistrement par fichier statique
static/registerSW.jsavec chemins absolus (navigator.serviceWorker.register('/sw.js', { scope: '/' })), lié dansapp.html. Ne PAS utiliser l'injection du plugin : sans@vite-pwa/sveltekitelle n'atteint pas le HTML pré-rendu, et leregisterSW.jsgénéré utilise un chemin relatif./sw.jscassé 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-Policyrestrictive. Livrée en deux formats :static/.htaccess(Apache/o2switch : +ErrorDocument 404 /404.html, redirect HTTPS, cache immutable/_app/et/fonts/) etstatic/_headers(Cloudflare Pages). RÚgles : aucun script inline (snippets thÚme/langue en fichiers externes) ;styleattributes OK (unsafe-inlinestyle) ; 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'$effectpour calculer),$props(traiter les props comme changeantes : valeurs dĂ©pendantes en$derived). - ĂvĂ©nements :
onclick={...}(jamaison: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,:globalen 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/$effectavec cleanup symétrique (kill()/revert()/destroy()/disconnect()). - Liens internes :
resolve()/ assets viaasset()de$app/paths; externes enrel="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Ă© â
(Ăchapper
npx @sveltejs/mcp list-sections # doc Svelte 5 npx @sveltejs/mcp get-documentation "$state,$derived" npx @sveltejs/mcp svelte-autofixer ./src/lib/MonComposant.svelte\$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)
- 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.
- 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é. - vite preview meurt si l'on rebuilde pendant qu'il tourne (il sert
.svelte-kit/output) â redĂ©marrer aprĂšs chaque build. - CSP
script-src 'self': anti-FOUC, lang-redirect et registerSW en fichiers externes statiques ; aucun inline. - Preview headless :
--virtual-time-budgetfige 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. - Puppeteer QA :
page.emulateMediaFeaturespour reduced-motion ; le tilt se teste pardispatchEvent(PointerEvent)(le scroll Lenis décalemouse.move). - 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#. - 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. - Lighthouse aprĂšs chaque passe (mobile) et corriger ce qui est remontĂ© â les deux points gagnĂ©s sur o-k-i.net :
aria-labelsur span (KineticText) et liens non soulignés. - Lighthouse
link-in-text-block: scopertext-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
- git d'abord : atlas n'avait aucun dĂ©pĂŽt â
git init+ commit de l'existant avant toute ligne de code. - 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).
- 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 (modulesoftware-modalrĂ©utilisĂ© en vol). - Conflits de ports : avant tout audit automatisĂ©, vĂ©rifier ce qui tourne (
vite previewd'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
- 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Ă©). - Fork de librairie â application : purger la dette de packaging (champs
exports/files/peerDependencies, changesets, publint, workflows CI de la lib) â lepackage.jsond'une app estprivate: trueet nu. - Le remote
origind'un fork pointe chez l'auteur original : le renommerupstreamavant 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 buildvert ·svelte-autofixerpropre sur les fichiers modifiés.- Si
paths.base:paths.relative: falseet 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-motionvé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: nonesur tout conteneur décoratif. - Layout app :
height: 100dvhsi « tout visible sans scroll » est exigĂ© âscrollHeight === innerHeightmesurĂ© 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.
langcorrect 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.