chore : import initial du dossier de travail (PRD, guides, recommandations doc2sveltekit)
Inclut SPECS_SVELTE.md : conventions Svelte 5/SvelteKit pour veille-ia, consolidées depuis doc2sveltekit-transition (playbook, charte OKI, best practices).
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# Dépendances et builds
|
||||||
|
node_modules/
|
||||||
|
app/build/
|
||||||
|
app/.svelte-kit/
|
||||||
|
|
||||||
|
# Données locales (dev) — en prod : /home/yunohost.app/veille-ia
|
||||||
|
app/data/
|
||||||
|
data/
|
||||||
|
|
||||||
|
# Environnement
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Divers
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
+225
@@ -0,0 +1,225 @@
|
|||||||
|
# SPECS_SVELTE.md — Conventions Svelte/SvelteKit pour « veille-ia »
|
||||||
|
|
||||||
|
> **Statut :** ce fichier PRÉVAUT sur tout choix technique frontend (cf. `prompt_kimi_cli_veille_ia.md`).
|
||||||
|
> Il consolide les recommandations de `doc2sveltekit-transition/` (playbook SvelteKit, charte OKI,
|
||||||
|
> best practices Svelte 5) adaptées à une **app privée d'administration** self-hosted YunoHost —
|
||||||
|
> pas un site vitrine public : pas de SEO, pas de motion « Awwwards », pas de PWA.
|
||||||
|
>
|
||||||
|
> En cas de conflit : ce document prime sur la recette générique pour la marque et le style ;
|
||||||
|
> le PRD (`plan_prd_veille_ia.md`) prime pour le fonctionnel ; la recette prime pour la méthode technique.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Stack
|
||||||
|
|
||||||
|
- **SvelteKit 2 + Svelte 5** (runes uniquement) + **TypeScript strict** (`strict: true`).
|
||||||
|
- **`@sveltejs/adapter-node`** — l'app tourne en service systemd derrière nginx (YunoHost).
|
||||||
|
- **Vite** (fourni par SvelteKit). Aucune configuration exotique.
|
||||||
|
- **CSS vanilla + tokens OKI** — **pas de Tailwind**, aucun framework CSS lourd (décision : recette §2.3, charte OKI).
|
||||||
|
- **Zod** pour la validation des schémas YAML (registre, profils, alertes).
|
||||||
|
- **js-yaml** pour la persistance YAML ; **simple-git** pour les commits automatiques du data_dir.
|
||||||
|
- **vitest** pour les tests (moteur de recommandation notamment).
|
||||||
|
- **eslint + prettier** (config SvelteKit standard).
|
||||||
|
- Gestionnaire de paquets : **npm** (lockfile `package-lock.json`, jamais mélangé avec pnpm/yarn).
|
||||||
|
- Node : **24 LTS** en dev comme en prod (ressource `nodejs` YunoHost alignée sur le catalogue : nodered, etherpad, hedgedoc utilisent `"24"`).
|
||||||
|
|
||||||
|
**Interdictions (recette §0, adaptées) :**
|
||||||
|
- Jamais de dépendance installée sans être importée et utilisée.
|
||||||
|
- Jamais de SSR désactivé globalement pour « faire marcher » une lib client.
|
||||||
|
- Jamais d'appel à un service tiers au chargement (fonts, CDN, analytics) — tout asset est self-hébergé.
|
||||||
|
- Aucune dépendance propriétaire ou source-available (licences OSI uniquement, projet AGPL-3.0).
|
||||||
|
|
||||||
|
## 2. Structure de dossiers (`app/`)
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── src/
|
||||||
|
│ ├── app.html # lang="fr", script theme.js externe (anti-FOUC)
|
||||||
|
│ ├── app.d.ts # typage Locals (user SSO), env
|
||||||
|
│ ├── hooks.server.ts # auth SSO (YNH_USER), CSP, paths.base
|
||||||
|
│ ├── lib/
|
||||||
|
│ │ ├── styles/
|
||||||
|
│ │ │ ├── oki-tokens.css # tokens charte OKI (copie versionnée, §4)
|
||||||
|
│ │ │ └── base.css # reset, primitives, gate reduced-motion
|
||||||
|
│ │ ├── server/ # code serveur uniquement (jamais importé côté client)
|
||||||
|
│ │ │ ├── config.ts # variables d'env (DATA_DIR, OLLAMA_URL, ALERTS_TOKEN…)
|
||||||
|
│ │ │ ├── registre.ts # lecture/écriture YAML + validation Zod
|
||||||
|
│ │ │ ├── profils.ts
|
||||||
|
│ │ │ ├── alertes.ts
|
||||||
|
│ │ │ ├── git.ts # simple-git : commit atomique à chaque écriture
|
||||||
|
│ │ │ └── recommandation.ts # moteur (pur, testable, sans I/O)
|
||||||
|
│ │ ├── components/ # Badge, Card, Tag, Icon, DataTable, FormField…
|
||||||
|
│ │ └── types.ts # types TS partagés (inférés des schémas Zod)
|
||||||
|
│ └── routes/
|
||||||
|
│ ├── +layout.svelte # skip-link, nav SSO-aware, flag-bar
|
||||||
|
│ ├── +layout.server.ts # expose l'utilisateur SSO aux pages
|
||||||
|
│ ├── +page.svelte # accueil (public minimal si non connecté)
|
||||||
|
│ ├── +error.svelte # erreurs designées (P5)
|
||||||
|
│ ├── registre/ # CRUD registre (protégé SSO)
|
||||||
|
│ ├── profils/ # CRUD profils (protégé SSO)
|
||||||
|
│ ├── recommander/ # UI moteur de recommandation
|
||||||
|
│ ├── alertes/ # inbox (phase 2)
|
||||||
|
│ └── api/
|
||||||
|
│ ├── recommander/+server.ts
|
||||||
|
│ └── alerts/+server.ts # POST protégé par token (phase 2)
|
||||||
|
├── static/
|
||||||
|
│ ├── fonts/ # Archivo + Inter woff2 self-hébergées + fonts.css
|
||||||
|
│ ├── theme.js # bascule thème clair/sombre anti-FOUC
|
||||||
|
│ └── icons.svg # sprite SVG (<symbol>), zéro emoji
|
||||||
|
├── package.json # private: true
|
||||||
|
├── svelte.config.js # adapter-node, paths
|
||||||
|
└── vite.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Règles :
|
||||||
|
- Tout code qui lit des fichiers, des variables d'env ou fait du fetch sortant vit dans `lib/server/`.
|
||||||
|
- Le moteur de recommandation est une fonction **pure** (entrées → sorties), testable sans serveur.
|
||||||
|
- Les données d'exemple/seed vivent dans `app/src/lib/server/seed/` et ne sont copiées dans le
|
||||||
|
`DATA_DIR` qu'au premier démarrage (jamais d'écrasement de données existantes).
|
||||||
|
|
||||||
|
## 3. Conventions Svelte 5 (obligatoires)
|
||||||
|
|
||||||
|
- **Runes only.** `$state` uniquement pour du réactif ; gros objets réassignés (réponses API) → `$state.raw`.
|
||||||
|
- `$derived` pour tout calcul ; `$derived.by` si l'expression est complexe. **Jamais `$effect` pour calculer.**
|
||||||
|
- `$effect` = escape hatch uniquement (sync avec lib externe), toujours avec cleanup symétrique.
|
||||||
|
- `$props()` en traitant les props comme changeantes : toute valeur dérivée d'une prop passe par `$derived`.
|
||||||
|
- Événements : `onclick={...}` (jamais `on:click`) ; listeners globaux via `<svelte:window>` / `<svelte:document>`.
|
||||||
|
- `{#each}` avec **clé unique** (id métier), jamais l'index ; pas de destructuration si mutation (`bind:`).
|
||||||
|
- Snippets `{#snippet}` / `{@render}` plutôt que `<slot>`.
|
||||||
|
- Classes conditionnelles : tableaux/objets style clsx dans `class={...}`, pas de `class:`.
|
||||||
|
- Liens internes via `resolve()` de `$app/paths` ; assets via `asset()`. Jamais de chemin en dur
|
||||||
|
(l'app peut être servie sous un sous-chemin YunoHost).
|
||||||
|
- `{@html}` interdit sauf contenu first-party, commenté comme tel.
|
||||||
|
- État partagé inter-composants : module `.svelte.ts` avec `$state` + fonctions — pas de stores legacy.
|
||||||
|
- **Règle des loads :** toute donnée lue côté serveur (YAML, API externe) passe par `+page.server.ts` /
|
||||||
|
`+layout.server.ts` ou des **form actions** — jamais de fetch direct vers le data_dir côté client.
|
||||||
|
- Mutations : **form actions SvelteKit** par défaut (progressive enhancement, fonctionne sans JS) ;
|
||||||
|
`fetch` JSON uniquement pour les endpoints API externes (`/api/*`).
|
||||||
|
- Persistance de préférences : `?? défaut`, jamais `|| défaut` (un `0`/`false` persisté est légitime).
|
||||||
|
- Validation systématique via `npx @sveltejs/mcp svelte-autofixer <fichier>` avant de finaliser un composant.
|
||||||
|
|
||||||
|
## 4. Style — tokens OKI (charte §2, copie versionnée)
|
||||||
|
|
||||||
|
Un seul fichier `src/lib/styles/oki-tokens.css` importé en premier dans `+layout.svelte` (aucune valeur
|
||||||
|
recopiée à la main dans les composants) :
|
||||||
|
|
||||||
|
```css
|
||||||
|
: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);
|
||||||
|
--or-oki: #FDB813; /* accent : SEULE couleur d'action */
|
||||||
|
--rouge-oki: #FF1654; /* signal — jamais un lien/bouton */
|
||||||
|
--vert-oki: #00D66C; /* succès, validation */
|
||||||
|
--or-clair: #FFE066; /* survol des boutons */
|
||||||
|
--muted: color-mix(in srgb, var(--blanc-creme) 70%, transparent);
|
||||||
|
--card-bg: rgba(255,255,255,0.03);
|
||||||
|
/* Typographie */
|
||||||
|
--font-display: 'Archivo', 'Arial Black', sans-serif;
|
||||||
|
--font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
/* Formes, espacements */
|
||||||
|
--radius-sm: 3px; --radius-md: 6px;
|
||||||
|
--border-card: 1px solid var(--line);
|
||||||
|
--border-btn: 2px solid var(--or-oki);
|
||||||
|
--space-1: 0.75rem; --space-2: 1rem; --space-3: 1.5rem; --space-4: 2rem; --space-5: 4rem;
|
||||||
|
--container: 1200px;
|
||||||
|
/* Motion */
|
||||||
|
--ease-ka: cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
--ease-syncope: cubic-bezier(0.65, 0, 0.35, 1);
|
||||||
|
--dur-tanbou: 120ms; --dur-mesure: 480ms; --dur-phrase: 960ms;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Sémantique des couleurs, adaptée au back-office (rôles **jamais permutés**) :
|
||||||
|
- **Or = agir** : boutons, liens, focus. Seule couleur d'action.
|
||||||
|
- **Vert = valider** : statut `production`, licence vérifiée, alerte traitée.
|
||||||
|
- **Rouge = signaler** : statut `mort`, urgence haute, erreur — jamais cliquable.
|
||||||
|
- **Badge « ⚠️ à vérifier »** (règle anti-hallucination du PRD) : fond or 8 % + bordure or 1 px
|
||||||
|
(tag OKI canonique) + texte crème ; rendu par un composant `<BadgeAVerifier>` dédié, utilisé
|
||||||
|
dès qu'un champ est `null`. Le pictogramme ⚠ fait partie du sprite SVG (règle zéro emoji ci-dessous).
|
||||||
|
|
||||||
|
Règles d'usage :
|
||||||
|
- Thème **sombre par défaut**, clair opt-in via `html.light-theme` posée par `static/theme.js`
|
||||||
|
(script externe, localStorage, anti-FOUC). Accents assombris en thème clair pour WCAG AA.
|
||||||
|
- Texte secondaire = crème 70–85 % d'opacité ; filets = blanc 10 %, jamais de gris plein.
|
||||||
|
- Titres h1–h4 : Archivo **majuscules**, `letter-spacing: -0.01em`. Boutons : Archivo 700 uppercase,
|
||||||
|
bordure 2 px or, survol `--or-clair`. Corps : Inter, `line-height: 1.5`.
|
||||||
|
- Cartes : fond `--card-bg`, liseré gauche 4 px d'accent, rayon ≤ 6 px. Angles nets partout.
|
||||||
|
- **Flag-bar** (6 px, 4 segments francs noir/or/vert/rouge) sous la navigation — **une occurrence
|
||||||
|
visible par écran maximum**.
|
||||||
|
- Iconographie : sprite `static/icons.svg` (`<symbol>` 24×24, stroke 2 px, `currentColor`) +
|
||||||
|
`<svg><use href="...#id"/></svg>`. **Zéro emoji en production** — y compris dans les badges et
|
||||||
|
messages d'état (le glyph ⚠ est fourni par le sprite, pas par un emoji Unicode).
|
||||||
|
- Fonts **self-hébergées** : woff2 dans `static/fonts/`, `@font-face` avec `font-display: swap`,
|
||||||
|
preload de la display uniquement. Aucun appel Google Fonts.
|
||||||
|
- Motion : micro-interactions sobres uniquement (`--dur-tanbou` sur hover/focus, `--dur-mesure`
|
||||||
|
sur transitions de vues). Uniquement `transform` + `opacity`. Durées lues depuis les tokens,
|
||||||
|
jamais hardcodées. **Gate unique `prefers-reduced-motion`** dans `base.css` (contenu statique complet).
|
||||||
|
- Styles scopés dans les composants ; personnalisation parent→enfant via custom properties ;
|
||||||
|
`:global` en dernier recours.
|
||||||
|
- Layout « app » : `height: 100dvh` + overflow par panneau si pertinent ; cibles tactiles ≥ 44 px.
|
||||||
|
- Listes longues (registre, inbox) : `content-visibility: auto` + `contain-intrinsic-size` d'abord,
|
||||||
|
pagination « charger plus » avant toute virtualisation.
|
||||||
|
|
||||||
|
## 5. Accessibilité (non négociable)
|
||||||
|
|
||||||
|
- Navigation clavier complète : tab order logique, **focus visible** (outline or 2 px), skip-link
|
||||||
|
« Aller au contenu » en premier élément du layout.
|
||||||
|
- Contrôles de formulaire tous labellisés (`<label for>`), erreurs de validation annoncées
|
||||||
|
(`aria-describedby`, `aria-invalid`).
|
||||||
|
- Contrastes WCAG AA vérifiés sur les deux thèmes (texte courant ≥ 4,5:1).
|
||||||
|
- Icônes décoratives `aria-hidden="true"` ; icônes seules dans un bouton → `aria-label`.
|
||||||
|
- Tableaux de données sémantiques (`<th scope>`), tri annoncé via `aria-sort`.
|
||||||
|
- Focus déplacé sur le `<h1>` après chaque navigation ; `lang="fr"` sur `<html>`.
|
||||||
|
- Le badge « à vérifier » ne repose jamais sur la couleur seule (pictogramme + texte).
|
||||||
|
- Zéro warning d'accessibilité svelte-check toléré.
|
||||||
|
|
||||||
|
## 6. i18n
|
||||||
|
|
||||||
|
- **Français d'abord** : tous les libellés UI en français, regroupés dans `src/lib/i18n/fr.ts`
|
||||||
|
(objet typé clé → libellé), jamais de chaîne en dur dans les templates.
|
||||||
|
- Structure prête pour créole/anglais : un fichier par locale, sélection par `data-locale`
|
||||||
|
(pas de routing i18n au MVP — app monoprivée).
|
||||||
|
- Microcopy signature en kréyòl admise (404, états vides) avec attribut `lang="gcf"` sur le span.
|
||||||
|
|
||||||
|
## 7. Sécurité & intégration YunoHost (côté app)
|
||||||
|
|
||||||
|
- **SSO :** `hooks.server.ts` lit le header **`Ynh-User`** injecté par SSOwat via `proxy_params_with_auth`
|
||||||
|
(conf nginx du package ; le header est vidé côté nginx avant d'être renseigné après authentification,
|
||||||
|
donc non spoofable). Sans header valide → seule la page d'accueil publique minimale est accessible ;
|
||||||
|
toutes les routes `/registre`, `/profils`, `/recommander`, `/alertes` et les form actions exigent un
|
||||||
|
utilisateur. En dev local, `DEV_USER` simule le header (documenté, ignoré en production).
|
||||||
|
- **CSP via la config SvelteKit** (`kit.csp`, mode `nonce`, dans `vite.config.ts` — SvelteKit ≥ 2.62
|
||||||
|
n'utilise plus `svelte.config.js`) :
|
||||||
|
`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'` + `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`.
|
||||||
|
**Aucun script inline** — `theme.js` est un fichier externe.
|
||||||
|
- **Sous-chemin :** l'app doit fonctionner servie sous `https://domaine.tld/veille` :
|
||||||
|
`paths.relative: false` dans `svelte.config.js`, base injectée par variable d'env au build/runtime,
|
||||||
|
et tous les liens via `resolve()`/`asset()`.
|
||||||
|
- **`POST /api/alerts`** : token `ALERTS_TOKEN` (header `Authorization: Bearer`), généré à l'install
|
||||||
|
YunoHost, jamais commité. Aucune route mutative sans vérification (SSO ou token).
|
||||||
|
- Toutes les entrées (YAML, formulaires, webhooks) validées par **Zod** côté serveur avant tout traitement.
|
||||||
|
- Journaux : `console` structuré (JSON), récupéré par journald via systemd.
|
||||||
|
|
||||||
|
## 8. Qualité — définition de « terminé » (chaque commit)
|
||||||
|
|
||||||
|
- `npm run check` (svelte-check) : **0 erreur, 0 warning**.
|
||||||
|
- `npm run lint` (eslint + prettier --check) : propre.
|
||||||
|
- `npm run test` (vitest) : vert — moteur de recommandation couvert (cas nominaux + cas limites :
|
||||||
|
licence `null`, seuil de revenus dépassé, modèle statut `mort`, champ `null` pénalisé).
|
||||||
|
- `npm run build` : vert.
|
||||||
|
- Grep de vérité avant livraison : zéro domaine tiers dans le build, zéro emoji dans le HTML buildé.
|
||||||
|
- Revue clavier + `prefers-reduced-motion` sur les pages ajoutées.
|
||||||
|
- Commits git **atomiques et explicites, en français** ; `README.md` à jour à chaque phase.
|
||||||
|
|
||||||
|
## 9. Explicitement hors périmètre (app privée)
|
||||||
|
|
||||||
|
- SEO, OG/Twitter cards, sitemap, robots — pas d'indexation, app derrière SSO.
|
||||||
|
- PWA / service worker / mode hors-ligne.
|
||||||
|
- Motion riche (Lenis, GSAP, WebGL, kinetic type) — la charte motion OKI s'applique en version sobre (§4).
|
||||||
|
- Imagerie afrofuturiste, footer fédéré public — interface utilitaire ; le favicon OKI suffit.
|
||||||
|
- Multi-utilisateurs, notifications push natives (PRD §10).
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
# CLASSEMENTS MODÈLES GÉNÉRATIFS — LICENCES & RECOMMANDATIONS D'USAGE
|
||||||
|
## Image · Vidéo · SVG — pour Visual Novel Ren'Py (CC BY-NC / CC BY-NC-ND), diffusion libre et prestations commerciales
|
||||||
|
|
||||||
|
**Date de vérification :** 1er août 2026
|
||||||
|
**Sources :** Artificial Analysis Arena, Lumenfall Arena, benchmark SVGenius, papier OmniSVG (NeurIPS 2025), licences officielles Hugging Face / éditeurs.
|
||||||
|
**Méthode anti-hallucination :** toute licence non vérifiable publiquement est marquée ⚠️ *« à vérifier »* plutôt qu'inventée.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. CADRE JURIDIQUE PRÉALABLE — CC + IA : le point que tout le monde oublie
|
||||||
|
|
||||||
|
### 0.1 Ta structure de licence
|
||||||
|
- **Jeu (contenu global) : CC BY-NC** — partage autorisé avec attribution, usage commercial interdit *pour les tiers*.
|
||||||
|
- **Histoire & personnages : CC BY-NC-ND** — idem + interdiction des œuvres dérivées *pour les tiers*.
|
||||||
|
|
||||||
|
### 0.2 Ce que ça implique vraiment
|
||||||
|
1. **Une licence CC n'existe que s'il y a un droit d'auteur à licencier.** Un asset 100 % IA brut n'est pas protégeable (TJ Paris 2022, Munich 2026, USCO 2025) → poser un logo CC dessus ne crée aucun droit. Ta licence CC ne vaut que pour ta **contribution humaine** : scénario, dialogues, code, montage, retouches, composition d'ensemble.
|
||||||
|
2. **CC BY-NC ne t'empêche pas, toi l'auteur, de vendre ton jeu.** NC lie les *réutilisateurs*, pas le licencieur. Tu peux vendre le VN sur itch.io/Steam ET le distribuer sous CC BY-NC — mais alors il te faut des **droits commerciaux sur les outputs IA** utilisés dedans (ta vente = usage commercial).
|
||||||
|
3. **Le ND sur personnages/histoire** est solide pour le texte (écrit humain) mais **inopposable sur un design de personnage 100 % IA** non retravaillé : n'importe qui pourrait le réutiliser légalement. → Model sheets refaites à la main / paint-over obligatoire pour que le ND morde.
|
||||||
|
4. **Compatibilité de chaîne :** les outputs de modèles à licence non-commerciale (Midjourney gratuit, FLUX.2 [dev]) ne doivent **jamais** entrer dans un produit vendu, même sous CC BY-NC, dès lors que TOI tu le vends.
|
||||||
|
5. **Steam/itch.io :** déclaration IA obligatoire (survey Steam / tag itch.io) quelle que soit la licence du jeu. CC n'y change rien.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. TOP IMAGE (avec Krea 2 intégré) — licences + usages
|
||||||
|
|
||||||
|
Classement consolidé : Artificial Analysis + Lumenfall Arena, juillet 2026.
|
||||||
|
|
||||||
|
| # | Modèle | Licence / accès | Output commercial ? | NSFW ? | Usage VN Ren'Py | Diffusion libre | Prestataire (vente) |
|
||||||
|
|---|---|---|---|---|---|---|---|
|
||||||
|
| 1 | **Nano Banana 2 / Pro** (Google) | Propriétaire (Gemini) | ✅ payant ; gratuit = entraînement sur tes données | ❌ interdit | 🟡 Backgrounds propres, édition d'assets | 🟡 OK | 🟡 OK mais dépendance SaaS ; archiver la ToS du jour |
|
||||||
|
| 2 | **GPT Image 2** (OpenAI) | Propriétaire, API | ✅ assignés | ❌ interdit | 🟡 Concepts, backgrounds | 🟡 OK | 🟢 Bon : assignation claire + indemnification API |
|
||||||
|
| 3 | **FLUX.2** | [klein] : **Apache 2.0** · [dev] : non-commercial · [pro] : API | klein ✅ · dev ❌ · pro ✅ | klein local : 🟢 aucun filtre | 🟢 **klein en local = pilier sprites/backgrounds, NSFW inclus** | 🟢 klein parfait | 🟢 klein oui ; jamais [dev] |
|
||||||
|
| 4 | **Krea 2** (Raw/Turbo) | Krea 2 Community License — permissive individus & équipes <50 sièges, garde-fous anti-NCII/CSAM obligatoires | ✅ à ton échelle | 🟡 local : pas de filtre imposé, mais garde-fous requis par la licence | 🟢 **Le meilleur rapport esthétique/souveraineté : LoRA personnages récurrents, moodboards, 2 s/2K, 10-12 Go VRAM, natif ComfyUI ≥ 0.25** | 🟢 Excellent | 🟢 Oui (<50 sièges) ; documenter les garde-fous |
|
||||||
|
| 5 | **Midjourney V8.1** | Propriétaire ; gratuit = CC BY-NC sans propriété ; >1M$ CA → Pro/Mega | ✅ payant uniquement | ❌ interdit | 🟡 DA exploratoire | 🟡 payant seulement | 🟢 DA premium client ; ⚠️ zéro indemnisation, litige Disney en cours |
|
||||||
|
| 6 | **Seedream 4.5 / 5.0** (ByteDance) | Propriétaire, API | ⚠️ ToS opaques | ❌ | 🟡 texte CJK dense | 🟡 | 🔴 Jamais pour exclusivité client (ToS floues) |
|
||||||
|
| 7 | **Ideogram 4** | Open-weight ⚠️ *licence exacte à vérifier sur HF* + API | ✅ API ; poids : selon licence | ❌ API ; local : selon licence | 🟢 **Typographie lisible = menus, titres, logos de chapitres** | 🟢 | 🟢 API oui ; local : vérifier d'abord |
|
||||||
|
| 8 | **Recraft V4.1** | Propriétaire | ✅ payant | ❌ | 🟡 UI du jeu, assets vectoriels | 🟡 | 🟢 Seul vrai vectoriel — facturable comme livrable SVG |
|
||||||
|
| 9 | **Imagen 4** (Google DeepMind) | Propriétaire (Vertex AI) | ✅ | ❌ | 🟡 photo-réalisme | 🟡 | 🟡 OK, doublon avec Nano Banana |
|
||||||
|
| 10 | **Qwen Image 2** (Alibaba) | **Apache 2.0** | ✅ total | 🟢 local : aucun filtre | 🟢 **Ton pipeline existant (Qwen-Image-Edit) — modifications d'assets, inpainting, cohérence persos** | 🟢 Parfait | 🟢 Parfait, zéro restriction |
|
||||||
|
| — | **Anima** (hors top 10, anime spécialisé) | CircleStone Non-Commercial (poids) + NVIDIA Open Model License | ✅ **outputs commercialisables explicitement (jeux et VN cités)** ; poids non monétisables | 🟢 local | 🟢 **Taillé pour l'esthétique VN anime ; 6 Go VRAM ; LoRA/merges revendables si tu es individu** | 🟢 | 🟢 Oui pour vendre des images ; ❌ pour embarquer le modèle dans un produit |
|
||||||
|
|
||||||
|
### Recommandation VN — stack image retenue
|
||||||
|
- **Base souveraine :** Krea 2 Turbo (génération) + Qwen Image 2 / Qwen-Image-Edit (édition, cohérence) + FLUX.2 [klein] (photoréalisme/NSFW) — 100 % local, 100 % commercial-friendly.
|
||||||
|
- **Anima** si direction anime marquée.
|
||||||
|
- **Ideogram/Recraft** pour les écrans-titres, menus, typo.
|
||||||
|
- **Paint-over Krita systématique** sur sprites et model sheets → rend la couche CC (et le ND sur les personnages) juridiquement mordante.
|
||||||
|
- **NSFW :** uniquement les modèles locaux sans filtre (Krea 2, FLUX.2 klein, Qwen, Anima). Aucun propriétaire.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. TOP VIDÉO — licences + usages
|
||||||
|
|
||||||
|
| # | Modèle | Licence / accès | Output commercial ? | Usage clips/anim (VN, trailers, films) | Diffusion libre | Prestataire (clips vendus) |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| 1 | **Veo 3.1** (Google) | Propriétaire | ✅ payant (dès ~$0.05/s Lite) | 🟢 Qualité cinéma, audio natif, 4K — trailer de VN | 🟡 payant | 🟢 Fiable, gros clients l'acceptent |
|
||||||
|
| 2 | **Kling 3.0 / Omni** (Kuaishou) | Propriétaire | ✅ payant (~$0.10/s) | 🟢 #1 Elo, lip-sync 5 langues — dialogues animés | 🟡 payant | 🟢 Bon rapport qualité/prix |
|
||||||
|
| 3 | **Runway Gen-4.5** | Propriétaire | ✅ payant ; gratuit ❌ | 🟢 Contrôle caméra pro — ⚠️ **entraîne sur tes inputs/outputs sauf Enterprise** ; filigrane C2PA | 🟡 | 🟡 Client à informer (C2PA + training) ; jamais d'assets confidentiels |
|
||||||
|
| 4 | **Seedance 2.0 / 2.5** (ByteDance) | Propriétaire | ⚠️ opaques | 🟢 Multi-shot 30 s/4K | 🟡 | 🔴 Éviter pour livrables contractuels |
|
||||||
|
| 5 | **Pika 2.x** | Propriétaire | ✅ payant | 🟡 Social/effects, lip-sync | 🟡 | 🟡 Clips courts réseaux sociaux |
|
||||||
|
| 6 | ~~Sora 2~~ | — | — | 🔴 **Mort : app fermée 26/04/2026, API coupée 24/09/2026** — ne plus intégrer nulle part | ❌ | ❌ |
|
||||||
|
| 7 | **Luma Ray3 / Ray3.14** | Propriétaire | ✅ payant | 🟡 Environnements cinématiques | 🟡 | 🟡 |
|
||||||
|
| 8 | **Hailuo 2.3** (MiniMax) | Propriétaire | ✅ payant (dès ~$10/mois) | 🟡 Bon quotidien pas cher | 🟡 | 🟡 Petits budgets |
|
||||||
|
| 9 | **Gemini Omni Flash** (Google) | Propriétaire | ✅ payant ($0.10/s) | 🟡 Itérations rapides conversationnelles | 🟡 | 🟡 Pré-viz / animatics |
|
||||||
|
| 10 | **Wan 2.1 / 2.2** (Alibaba) | **Apache 2.0** | ✅ total | 🟢 **Ta voie souveraine : cinématiques de jeu, boucles d'animation, clips en local, aucune censure** | 🟢 Parfait | 🟢 Parfait — 0 €/clip, délais seuls limites |
|
||||||
|
| — | **HunyuanVideo 1.5** (mention) | Apache 2.0 | ✅ | 🟢 Alternative open-weight Tencent | 🟢 | 🟢 |
|
||||||
|
| — | **LTX-2.3** (mention) | Duale Community/Commercial ⚠️ *seuil à vérifier* | ✅ sous seuil | 🟡 Très rapide, audio intégré | 🟡 | 🟡 vérifier le seuil avant contrat |
|
||||||
|
|
||||||
|
### Recommandation VN — stack vidéo
|
||||||
|
- **Cinématiques et boucles d'arrière-plan du VN : Wan 2.2 local** — cohérent avec tes assets Krea/Qwen, aucune ToS de contenu (NSFW soft inclus), 0 €.
|
||||||
|
- **Trailer marketing :** si délai court et budget ponctuel → un mois de Kling ou Veo ; sinon Wan + montage DaVinci.
|
||||||
|
- **Clips animés pour clients :** Wan local = marge maximale ; Runway/Kling payants si le client exige une qualité/datation précise — mais **clause dans le devis** (outil IA utilisé, C2PA éventuel, pas de garantie d'exclusivité sur output 100 % IA).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2bis. MAJ 1er août 2026 — MiniMax H3 & FLUX 3 (sorties post-classement)
|
||||||
|
|
||||||
|
### MiniMax H3 (31/07/2026) — entre directement #2-3 vidéo, #1 open-weight dès sortie des poids
|
||||||
|
- **Omni-modal** : texte/image/vidéo/audio → vidéo 15 s 2K @24fps, son stéréo natif, multi-shot, édition par instruction.
|
||||||
|
- **Artificial Analysis** : #1 édition vidéo, #2 text-to-video (derrière Gemini Omni Flash), #3 image-to-video.
|
||||||
|
- **Prix API** : $0.13/s en 2K (~$7.80/min audio inclus) — 3× moins cher que Kling 3.0/Seedance 2.0.
|
||||||
|
- **Licence** : poids annoncés sous **MiniMax Community License** — commercial OK **<20 M$ CA** avec **attribution proéminente obligatoire**. ⚠️ Pas Apache : ligne de crédit MiniMax requise dans crédits de jeu/film et livrables clients ; poids pas encore publiés à la date de ce document (vérifier le repo HF `MiniMaxAI`).
|
||||||
|
- **Verdict :** futur pilier vidéo souverain, devant Wan 2.2 en qualité, derrière en simplicité de licence. Remplace Hailuo 2.3 (#8 du tableau) comme option MiniMax.
|
||||||
|
|
||||||
|
### FLUX 3 (23/07/2026) — Early Access fermée, pas de poids, pas de prix
|
||||||
|
- **Multimodal unifié** : image + vidéo 20 s audio natif + prédiction d'action (robotique). 4 lignes : FLUX 3 Video / Image / Action / **Dev (open-weight multimodal — vidéo+audio+image dans un seul checkpoint, du jamais vu en open)**.
|
||||||
|
- **Benchmarks auto-rapportés sans méthodologie publiée** : 60 % vs Kling v3 Pro, 77 % vs Runway Gen-4.5, mais 52 % (égalité) vs Omni Flash et Seedance 2.0 → prudence.
|
||||||
|
- **Licence** : ⚠️ **inconnue à ce jour**. FLUX 3 Dev annoncé « plus tard en 2026 » ; si BFL suit le schéma FLUX.2 (klein 4B Apache 2.0, 9B non-commercial, dev à outputs commercialisables), les outputs devraient rester exploitables — **à revérifier à la sortie, ne rien présupposer en contrat client**.
|
||||||
|
- **Verdict :** hors recommandation production tant que Early Access ; FLUX 3 Dev = veille prioritaire fin 2026, potentiel graal unifié local.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. TOP SVG — licences + usages
|
||||||
|
|
||||||
|
Pas de leaderboard public consolidé ; classement croisé SVGenius / OmniSVG / tests outils 2026. Deux familles.
|
||||||
|
|
||||||
|
### 3.1 Modèles spécialisés
|
||||||
|
| # | Modèle | Licence | Verdict usage |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **OmniSVG** (3B/7B, base Qwen2.5-VL) | **Apache 2.0** | 🟢 **Le choix : local (≈17 Go VRAM), icônes UI du VN, assets Godot, exploration de logos — puis refonte Inkscape** |
|
||||||
|
| 2 | **StarVector** (8B) | ⚠️ recherche, *licence à vérifier* | 🟡 Image→SVG (vectorisation d'icônes) |
|
||||||
|
| 3 | **LLM4SVG** | ⚠️ recherche | 🟡 précision spatiale, pas de release grand public claire |
|
||||||
|
| 4 | **Chat2SVG** | ⚠️ recherche | 🟡 pipeline 2 étages, démonstrateur |
|
||||||
|
| 5 | **IconShop** | ⚠️ recherche | 🟡 icônes **noir & blanc uniquement** |
|
||||||
|
| 6 | **SVGDreamer / VectorFusion / NeuralSVG** | ⚠️ code recherche | 🔴 4-10 min/SVG, qualité irrégulière — pas production |
|
||||||
|
|
||||||
|
### 3.2 LLM généralistes (écriture de code SVG)
|
||||||
|
| # | Modèle | Licence | Verdict usage |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 7 | **Claude 4.x** | Propriétaire | 🟢 Meilleur en compréhension/édition de SVG existants (debug, refactor d'icônes) |
|
||||||
|
| 8 | **GPT-5.x** | Propriétaire | 🟢 Meilleur en génération pure de SVG |
|
||||||
|
| 9 | **DeepSeek-R1** | MIT (open-weight) | 🟢 Meilleur open en raisonnement SVG — **ta voie locale** |
|
||||||
|
| 10 | **QwQ-32B / Qwen3-32B** | Apache 2.0 | 🟢 Édition de style au-dessus de GPT-4o sur le benchmark — local aussi |
|
||||||
|
|
||||||
|
### Recommandation SVG
|
||||||
|
- **VN/UI/Godot :** OmniSVG local pour la génération → nettoyage et refonte dans Inkscape (obligatoire : SVG générés souvent verbeux, paths non optimisés).
|
||||||
|
- **Logos (AKILPA, Denkyem X, clients) :** règle inchangée — IA = exploration, livrable = vectorisation humaine, sinon logo non protégeable et marque indéposable.
|
||||||
|
- **Automatisation :** Qwen3-32B/DeepSeek-R1 local pour générer des variantes d'icônes en batch dans tes pipelines web.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. MATRICES DE DÉCISION PAR SCÉNARIO
|
||||||
|
|
||||||
|
### Scénario A — Diffusion libre (contenu gratuit, CC, communauté)
|
||||||
|
| Besoin | Choix n°1 | Choix n°2 | À éviter |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Image | Krea 2 local | Qwen Image 2 / FLUX.2 klein | Midjourney gratuit (outputs CC BY-NC que tu ne possèdes pas) |
|
||||||
|
| Vidéo | Wan 2.2 local | HunyuanVideo 1.5 | Tiers gratuits SaaS (watermarks, pas de droits) |
|
||||||
|
| SVG | OmniSVG local | Qwen3-32B local | IconShop (N&B seul) |
|
||||||
|
| Musique | Banques CC0 (Musopen, Freesound) | ACE-Step / Stable Audio Open | Suno gratuit (non commercial), Udio (export mort) |
|
||||||
|
| Texte | Kimi K2.5 / Qwen3 / DeepSeek local | — | API propriétaires pour le texte final NSFW |
|
||||||
|
|
||||||
|
**Logique :** en diffusion libre, le coût et la souveraineté dominent → full open-weight local. Les licences Apache 2.0/MIT/Community te laissent tout faire, y compris changer d'avis et vendre plus tard.
|
||||||
|
|
||||||
|
### Scénario B — Prestataire : vente d'images
|
||||||
|
| Élément | Recommandation |
|
||||||
|
|---|---|
|
||||||
|
| Production | Krea 2 / Qwen Image 2 / FLUX.2 klein locaux + **paint-over documenté** (c'est ce que tu vends : ton travail) |
|
||||||
|
| Si SaaS demandé | Midjourney payant (DA premium, ⚠️ pas d'indemnisation) ou GPT Image 2 API (assignation + indemnification) |
|
||||||
|
| Contrat | Clause outils IA + **jamais de garantie d'exclusivité/copyright sur output 100 % IA brut** ; cession limitée à ta contribution |
|
||||||
|
| À éviter | Seedream (ToS opaques), tout tier gratuit, livrer un logo 100 % IA |
|
||||||
|
|
||||||
|
### Scénario C — Prestataire : clips animés vendus
|
||||||
|
| Élément | Recommandation |
|
||||||
|
|---|---|
|
||||||
|
| Production | Wan 2.2 local (marge max) ; Kling 3.0 ou Veo 3.1 payants si exigence qualité/délai |
|
||||||
|
| Runway | Uniquement si client informé : training par défaut + filigrane C2PA |
|
||||||
|
| Musique du clip | Banque à licence claire (Artlist, Epidemic) ou Suno Pro **sans promettre l'exclusivité** ; jamais Udio |
|
||||||
|
| Contrat | Mention déclaration IA YouTube/plateforme à la charge de qui + horodatage des rushes |
|
||||||
|
| À éviter | Sora 2 (mort), Seedance (opaque), promettre un droit d'auteur sur du 100 % IA |
|
||||||
|
|
||||||
|
### Scénario D — Jeu vendu sur Steam / itch.io (le VN et les jeux Godot)
|
||||||
|
| Élément | Recommandation |
|
||||||
|
|---|---|
|
||||||
|
| Stack assets | 100 % local open-weight (Krea 2, Qwen, FLUX.2 klein, Wan, OmniSVG) → chaîne la plus défendable |
|
||||||
|
| Déclarations | Steam survey (assets IA visibles joueur + marketing) ; tag itch.io « AI-Generated » sous peine de désindexation |
|
||||||
|
| NSFW | Local uniquement ; Steam = patch externe ; itch.io = tag adulte ; jamais de génération IA live in-game (interdit Steam) |
|
||||||
|
| Musique | CC0 ou Suno Pro assumé + crédits ; pas d'exclusivité revendiquée |
|
||||||
|
| Seuils de licence | Midjourney >1M$, SD 3.5 >1M$, Krea 2 >50 sièges : hors de ta zone, mais à revérifier si un jeu explose |
|
||||||
|
| LICENSE.txt | Modèle enrichi (clause « Génération par IA ») — cf. ton guide §8 |
|
||||||
|
| CC BY-NC(-ND) | Ne s'applique valablement qu'à ta contribution humaine ; le mentionner clairement dans le fichier licence pour éviter le malentendu |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. RÈGLES TRANSVERSALES (rappel condensé)
|
||||||
|
|
||||||
|
1. **Tout ce qui est vendu** → modèle avec droit commercial explicite, vérifié le jour J, ToS archivée en PDF.
|
||||||
|
2. **Tout ce qui doit être protégé** (personnages ND, logos, charte AKILPA) → apport humain substantiel + preuves (itérations, git, horodatage).
|
||||||
|
3. **Tout ce qui est NSFW ou sensible** → local open-weight, jamais d'API propriétaire.
|
||||||
|
4. **Tout ce qui vient d'un client** (données, assets) → API no-training payante ou local ; Runway et tiers gratuits exclus.
|
||||||
|
5. **Seuils de licence** (revenus, sièges) → à revérifier à chaque changement d'échelle.
|
||||||
|
6. **Plateformes** → déclarations IA systématiques (Steam, itch.io, YouTube) ; la transparence documentée protège, le silence expose.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Vérifié au 1er août 2026. Les classements bougent chaque mois, les licences moins souvent — revérifier les deux avant tout projet à enjeu. Document stratégique, pas un avis juridique.*
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
|
||||||
|
|
||||||
|
## Available Svelte MCP Tools:
|
||||||
|
|
||||||
|
### 1. list-sections
|
||||||
|
|
||||||
|
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
|
||||||
|
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
|
||||||
|
|
||||||
|
### 2. get-documentation
|
||||||
|
|
||||||
|
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
|
||||||
|
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
|
||||||
|
|
||||||
|
### 3. svelte-autofixer
|
||||||
|
|
||||||
|
Analyzes Svelte code and returns issues and suggestions.
|
||||||
|
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
|
||||||
|
|
||||||
|
### 4. playground-link
|
||||||
|
|
||||||
|
Generates a Svelte Playground link with the provided code.
|
||||||
|
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# ORGANISATION KA INTERNATIONALE — Guide de marque
|
||||||
|
|
||||||
|
Source mesurée : https://o-k-i.net/ (CSS et HTML réels, juillet 2026).
|
||||||
|
OKI développe des applications web sur mesure pour les associations, collectifs
|
||||||
|
et organisations militantes, avec des tarifs adaptés à leurs moyens. L'identité
|
||||||
|
est **afrofuturiste caribéenne** : un drapeau, pas une charte corporate.
|
||||||
|
|
||||||
|
## Identité en une phrase
|
||||||
|
|
||||||
|
Un studio web militant caribéen dont la marque est un drapeau pan-africain —
|
||||||
|
noir, or, vert, rouge — posé sur un fond presque noir, en capitales Archivo.
|
||||||
|
|
||||||
|
## Couleurs
|
||||||
|
|
||||||
|
Le **thème sombre est l'identité par défaut**. Le thème clair existe mais comme
|
||||||
|
variante opt-in (`html.light-theme`) qui inverse les rôles et assombrit les
|
||||||
|
accents pour WCAG AA.
|
||||||
|
|
||||||
|
| Rôle | Couleur | Hex | Variable d'origine |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| background | Noir OKI | `#0D0D0D` | `--noir-oki` |
|
||||||
|
| surface | Noir profond | `#1A0F1A` | `--noir-profond` |
|
||||||
|
| foreground | Blanc crème | `#FFF8E7` | `--blanc-creme` |
|
||||||
|
| muted | Crème atténué | `#B6B1A6` | dérivé : blanc crème à 70 % (le site utilise `opacity: 0.7–0.85`) |
|
||||||
|
| border | Ligne | `#262626` | dérivé : `rgba(255,255,255,0.1)` (`--line`) |
|
||||||
|
| accent | Or OKI | `#FDB813` | `--or-oki` |
|
||||||
|
| accent-secondary | Rouge OKI | `#FF1654` | `--rouge-oki` |
|
||||||
|
|
||||||
|
Palette étendue mesurée (à utiliser avec parcimonie, décorations et sémantique) :
|
||||||
|
vert OKI `#00D66C` (succès, dons mensuels), turquoise caraïbes `#00CED1`,
|
||||||
|
jaune soleil `#FFD700`, orange flamme `#FF6B35`, violet nuit `#6B2D5C`,
|
||||||
|
bleu océan `#0077BE`, or clair `#FFE066` (survol des boutons), gris sombre `#2D1B2E`.
|
||||||
|
|
||||||
|
Règles d'usage :
|
||||||
|
|
||||||
|
- L'**or `#FDB813` porte toute l'interaction** : liens, boutons, tags, survols,
|
||||||
|
focus. C'est la seule couleur d'action.
|
||||||
|
- Le rouge `#FF1654` signale et ponctue ; le vert `#00D66C` valide. Ne jamais
|
||||||
|
les substituer à l'or pour un lien ou un bouton principal.
|
||||||
|
- Texte secondaire : blanc crème à 70–85 % d'opacité, pas une couleur dédiée.
|
||||||
|
- Filets : blanc à 10 % d'opacité (`rgba(255,255,255,0.1)`), jamais de gris plein.
|
||||||
|
|
||||||
|
### Le « flag-bar » — signature graphique
|
||||||
|
|
||||||
|
Bandeau de 6 px à 4 segments **francs** : noir 0–25 %, or 25–50 %, vert 50–75 %,
|
||||||
|
rouge 75–100 %. Arrêts nets, **jamais de dégradé**. Il se place en bas de la
|
||||||
|
navigation et peut ponctuer un héros ou un pied de page. C'est l'élément de
|
||||||
|
marque le plus reconnaissable : l'utiliser une fois par écran, pas plus.
|
||||||
|
|
||||||
|
## Typographie
|
||||||
|
|
||||||
|
- **Display : Archivo** (600–900), MAJUSCULES, `letter-spacing: -0.01em`,
|
||||||
|
repli `'Arial Black', sans-serif`. Tous les titres (h1–h4) et les boutons.
|
||||||
|
- **Body : Inter** (400–800), repli `-apple-system, BlinkMacSystemFont, sans-serif`.
|
||||||
|
Texte courant, tags, formulaires. `line-height: 1.5`.
|
||||||
|
- Les deux familles sont auto-hébergées dans `fonts/` (woff2) et déclarées dans
|
||||||
|
`fonts/fonts.css` ; Google Fonts :
|
||||||
|
`https://fonts.googleapis.com/css2?family=Archivo:wght@600;700;800;900&family=Inter:wght@400;500;600;700;800&display=swap`.
|
||||||
|
- Boutons : Archivo 700, uppercase, `letter-spacing: 0.03em`, bordure 2 px.
|
||||||
|
|
||||||
|
## Voix et ton
|
||||||
|
|
||||||
|
Vouvoiement direct et empathique, phrases courtes. On parle d'abord de la cause
|
||||||
|
du visiteur — « Vous luttez, vous organisez, vous mobilisez » — puis de l'outil.
|
||||||
|
La technique sert la lutte, jamais l'inverse.
|
||||||
|
|
||||||
|
- **À dire :** sur mesure · tarifs adaptés à vos moyens · organisations engagées ·
|
||||||
|
transition numérique · écosystème numérique libre · à la hauteur.
|
||||||
|
- **À éviter :** « template générique », jargon corporate/marketing,
|
||||||
|
« clé en main », startup-speak. (Inféré du positionnement du site : OKI se
|
||||||
|
définit explicitement contre le template et les géants du web.)
|
||||||
|
- Piliers : ORGANISATION KA · INTERNATIONALE · Des solutions pour chaque besoin ·
|
||||||
|
Le budget ne doit jamais être un frein.
|
||||||
|
|
||||||
|
## Imagerie
|
||||||
|
|
||||||
|
Illustration numérique afrofuturiste caribéenne : scènes au coucher de soleil,
|
||||||
|
personnages caribéens, pochettes musicales typographiées (DJANGOKAM, l'artiste
|
||||||
|
IA caribéen du collectif). Couleurs chaudes saturées sur fonds sombres.
|
||||||
|
|
||||||
|
- Échantillons réels dans `imagery/` (voir `brand.json → imagery.samples`).
|
||||||
|
- **Jamais** de photo de banque d'images corporate ni d'illustration générique
|
||||||
|
de start-up. Jamais d'icône ou de logo en guise d'image.
|
||||||
|
|
||||||
|
## Layout et posture
|
||||||
|
|
||||||
|
- **Angles nets** : rayons 3 px (boutons, tags, badges) à 6 px (cartes). Aucune
|
||||||
|
forme organique, aucune bulle très arrondie.
|
||||||
|
- Bordures 1 px `rgba(255,255,255,0.1)` ; boutons bordure 2 px.
|
||||||
|
- Cartes : fond blanc à 3 %, liseré gauche 4 px d'accent (vert ou or), survol
|
||||||
|
`translateY(-2px à -4px)` + bordure d'accent.
|
||||||
|
- Tags : fond or à 8 % + bordure or 1 px, rayon 3 px.
|
||||||
|
- Espacement : échelle rem 0.75 / 1 / 1.5 / 2 / 4 ; sections `4rem 2rem` ;
|
||||||
|
conteneur max 1200 px ; grille `repeat(auto-fit, minmax(300px, 1fr))`.
|
||||||
|
- Navigation fixe, fond noir OKI, flag-bar en bord inférieur.
|
||||||
|
|
||||||
|
## Logotype
|
||||||
|
|
||||||
|
Monogramme « OKI » : arche or, point vert, accent rouge sur noir. Fichier
|
||||||
|
canonique `logos/favicon-1.png` (512×512, transparent — aussi og:image et
|
||||||
|
apple-touch-icon) ; déclinaisons `logos/favicon-0.ico` et
|
||||||
|
`assets/maskable_oki.png`. Les `logos/header-img-*` sont des marques de
|
||||||
|
**partenaires** (Joukawouvè, Aktivist Vybz, cyber-mawonaj, KA UBUNTU) :
|
||||||
|
provenance conservée, ne pas les utiliser comme logo OKI.
|
||||||
|
|
||||||
|
## Limites connues
|
||||||
|
|
||||||
|
- `muted` et `border` sont des valeurs **dérivées** (le site travaille en
|
||||||
|
opacités, pas en couleurs pleines) ; tout le reste est mesuré.
|
||||||
|
- L'inventaire d'images larges du site se limite à 5 visuels non-logo
|
||||||
|
(1 portrait + 3 pochettes + 1 affiche partenaire) ; les autres images du site
|
||||||
|
sont des logos de partenaires, exclus des échantillons.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Pousser un projet sur Gitea (labola.o-k-i.net)
|
||||||
|
|
||||||
|
Mémo rédigé le 2026-07-19 après la mise en ligne de `gwada-sirius`.
|
||||||
|
À relire avant de pousser un autre projet sur `https://labola.o-k-i.net/cyber-mawonaj`.
|
||||||
|
|
||||||
|
## Prérequis déjà en place (setup unique, fait)
|
||||||
|
|
||||||
|
- **Instance Gitea** : `https://labola.o-k-i.net`, namespace `cyber-mawonaj`.
|
||||||
|
- **Clé SSH dédiée** : `~/.ssh/id_ed25519_gitea` (sans passphrase), enregistrée dans
|
||||||
|
Gitea → Paramètres → Clés SSH (`https://labola.o-k-i.net/user/settings/keys`).
|
||||||
|
- **Entrée dans `~/.ssh/config`** pour utiliser cette clé automatiquement :
|
||||||
|
|
||||||
|
```
|
||||||
|
Host labola.o-k-i.net
|
||||||
|
IdentityFile ~/.ssh/id_ed25519_gitea
|
||||||
|
IdentitiesOnly yes
|
||||||
|
```
|
||||||
|
|
||||||
|
- ⚠️ `id_rsa` (et les autres clés du compte) ont une passphrase et **ne fonctionnent
|
||||||
|
pas** en non-interactif (pas d'agent SSH). Toujours utiliser `id_ed25519_gitea`.
|
||||||
|
- Test de connexion : `ssh -T git@labola.o-k-i.net` doit répondre
|
||||||
|
`Hi there, cyber-mawonaj! ...`
|
||||||
|
|
||||||
|
## Procédure pour un nouveau projet
|
||||||
|
|
||||||
|
1. **Créer le dépôt vide sur Gitea** (interface web) :
|
||||||
|
- Aller sur `https://labola.o-k-i.net/repo/create`
|
||||||
|
- Propriétaire : `cyber-mawonaj` ; nom : celui du projet
|
||||||
|
- ⚠️ **Décocher « Initialiser le dépôt »** (pas de README/licence/.gitignore),
|
||||||
|
sinon le premier push est refusé (historiques différents).
|
||||||
|
2. **Côté local**, dans le dossier du projet :
|
||||||
|
- Vérifier l'état : `git status` (commits présents, copie propre).
|
||||||
|
- Vérifier que `.gitignore` exclut `node_modules/`, les builds (`_site/`, `dist/`…), `.env`.
|
||||||
|
3. **Ajouter le remote** (garder `origin` si un remote GitHub existe déjà) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote add gitea git@labola.o-k-i.net:cyber-mawonaj/<nom-du-projet>.git
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Pousser** (demander confirmation à l'utilisateur avant) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push -u gitea main # ou master, vérifier avec `git branch --show-current`
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Vérifier** : `git ls-remote gitea main` doit afficher le même hash que `git rev-parse main`.
|
||||||
|
|
||||||
|
## Effets de bord à connaître
|
||||||
|
|
||||||
|
- Après `git push -u gitea main`, la branche suit `gitea/main` : un simple `git push`
|
||||||
|
ira vers Gitea. Pour GitHub : `git push origin main` explicite.
|
||||||
|
- Si la clé SSH est refusée (`Permission denied (publickey)`) : vérifier que la clé
|
||||||
|
est bien enregistrée dans Gitea et que l'entrée `Host labola.o-k-i.net` existe dans
|
||||||
|
`~/.ssh/config`.
|
||||||
|
- Alternative à l'interface web pour créer le dépôt : l'API Gitea avec un token
|
||||||
|
(`POST /api/v1/orgs/cyber-mawonaj/repos`), non utilisée ici.
|
||||||
@@ -0,0 +1,705 @@
|
|||||||
|
# PRD : Recommandations Web — AI Overview & SEO Top-Tier
|
||||||
|
## Pour agents LLM — Refonte / Création de site web
|
||||||
|
|
||||||
|
**Version :** 1.0 — Juillet 2026
|
||||||
|
**Auteur :** AKILPA / o-k-i
|
||||||
|
**Langue :** Français (terminologie technique en anglais conservée)
|
||||||
|
**Licence :** CC BY-SA 4.0 — Libre réutilisation avec attribution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Philosophie directrice
|
||||||
|
|
||||||
|
> **« Penser machine d'abord, humain ensuite. »**
|
||||||
|
|
||||||
|
Google AI Overview ne "lit" pas un site comme un humain. Il l'ingère, le fragmente, le recompose. Chaque page doit être conçue comme une **base de connaissances structurée** avant d'être une expérience utilisateur.
|
||||||
|
|
||||||
|
**Règle d'or :** Si un LLM ne peut pas extraire une réponse directe en 3 phrases depuis ta page, Google AI Overview ne te citera pas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Architecture technique fondamentale
|
||||||
|
|
||||||
|
### 1.1 HTML sémantique strict
|
||||||
|
|
||||||
|
| Élément | Usage obligatoire | Anti-pattern à bannir |
|
||||||
|
|---------|-------------------|----------------------|
|
||||||
|
| `<main>` | Contenu principal unique par page | `<div id="content">` |
|
||||||
|
| `<article>` | Contenu autonome (blog, fiche, news) | `<div class="post">` |
|
||||||
|
| `<section>` | Regroupement thématique avec heading | `<div class="section">` |
|
||||||
|
| `<header>` | En-tête de page OU d'article | `<div class="header">` |
|
||||||
|
| `<footer>` | Pied de page OU d'article | `<div class="footer">` |
|
||||||
|
| `<nav>` | Navigation principale | `<div class="menu">` |
|
||||||
|
| `<aside>` | Contenu complémentaire | `<div class="sidebar">` |
|
||||||
|
| `<time datetime="...">` | Dates publiées/modifiées | Texte brut sans attribut |
|
||||||
|
| `<address>` | Coordonnées organisation | `<p>` générique |
|
||||||
|
| `<figure>` + `<figcaption>` | Images avec légende | `<div class="img-wrapper">` |
|
||||||
|
|
||||||
|
**Impératif :** Aucun `<div>` ou `<span>` ne doit remplacer un élément sémantique disponible.
|
||||||
|
|
||||||
|
### 1.2 Hiérarchie de headings (H1-H6)
|
||||||
|
|
||||||
|
```
|
||||||
|
H1 : Titre principal de la page (UN SEUL)
|
||||||
|
H2 : Section majeure
|
||||||
|
H3 : Sous-section
|
||||||
|
H4 : Détail
|
||||||
|
H2 : Section majeure suivante
|
||||||
|
H3 : Sous-section
|
||||||
|
```
|
||||||
|
|
||||||
|
**Règles :**
|
||||||
|
- H1 = question principale que la page résout
|
||||||
|
- H2 = questions fréquentes liées (format interrogatif privilégié)
|
||||||
|
- H3 = réponses structurées ou sous-thèmes
|
||||||
|
- Jamais de saut (H2 → H4 sans H3)
|
||||||
|
- Jamais de headings pour du style (utiliser CSS)
|
||||||
|
|
||||||
|
### 1.3 Attributs ARIA stratégiques
|
||||||
|
|
||||||
|
```html
|
||||||
|
<article aria-labelledby="titre-article">
|
||||||
|
<h2 id="titre-article">Titre</h2>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<nav aria-label="Navigation principale">
|
||||||
|
<!-- liens -->
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<aside aria-label="Contenu connexe">
|
||||||
|
<!-- contenu -->
|
||||||
|
</aside>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Schema.org — Le squelette invisible
|
||||||
|
|
||||||
|
### 2.1 Types obligatoires par type de page
|
||||||
|
|
||||||
|
| Type de page | Schema.org principal | Schémas secondaires |
|
||||||
|
|--------------|----------------------|---------------------|
|
||||||
|
| Page d'accueil | `Organization` + `WebSite` | `LocalBusiness` si applicable |
|
||||||
|
| Article de blog | `BlogPosting` | `Article`, `Person` (auteur) |
|
||||||
|
| Page produit | `Product` | `Offer`, `Review`, `AggregateRating` |
|
||||||
|
| Page service | `Service` | `Organization` (fournisseur) |
|
||||||
|
| Page événement | `Event` | `Place`, `Offer` |
|
||||||
|
| Page FAQ | `FAQPage` | `Question` + `Answer` (xN) |
|
||||||
|
| Page HowTo | `HowTo` | `HowToStep`, `HowToTool`, `HowToSupply` |
|
||||||
|
| Page personne | `Person` | `Organization` (affiliation) |
|
||||||
|
| Page contact | `ContactPage` | `Organization` |
|
||||||
|
| Page mentions légales | `AboutPage` | `Organization` |
|
||||||
|
|
||||||
|
### 2.2 JSON-LD template générique (à adapter)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@graph": [
|
||||||
|
{
|
||||||
|
"@type": "Organization",
|
||||||
|
"@id": "https://example.com/#organization",
|
||||||
|
"name": "Nom de l'organisation",
|
||||||
|
"url": "https://example.com",
|
||||||
|
"logo": {
|
||||||
|
"@type": "ImageObject",
|
||||||
|
"url": "https://example.com/logo.png",
|
||||||
|
"width": 512,
|
||||||
|
"height": 512
|
||||||
|
},
|
||||||
|
"sameAs": [
|
||||||
|
"https://twitter.com/compte",
|
||||||
|
"https://linkedin.com/company/compte",
|
||||||
|
"https://github.com/compte"
|
||||||
|
],
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"streetAddress": "Adresse",
|
||||||
|
"addressLocality": "Ville",
|
||||||
|
"postalCode": "Code postal",
|
||||||
|
"addressCountry": "FR"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "WebSite",
|
||||||
|
"@id": "https://example.com/#website",
|
||||||
|
"url": "https://example.com",
|
||||||
|
"name": "Nom du site",
|
||||||
|
"publisher": {
|
||||||
|
"@id": "https://example.com/#organization"
|
||||||
|
},
|
||||||
|
"potentialAction": {
|
||||||
|
"@type": "SearchAction",
|
||||||
|
"target": {
|
||||||
|
"@type": "EntryPoint",
|
||||||
|
"urlTemplate": "https://example.com/search?q={search_term_string}"
|
||||||
|
},
|
||||||
|
"query-input": "required name=search_term_string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": "https://example.com/page/#webpage",
|
||||||
|
"url": "https://example.com/page",
|
||||||
|
"name": "Titre de la page",
|
||||||
|
"description": "Meta description de la page",
|
||||||
|
"isPartOf": {
|
||||||
|
"@id": "https://example.com/#website"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"@id": "https://example.com/#organization"
|
||||||
|
},
|
||||||
|
"datePublished": "2026-07-24T00:00:00+00:00",
|
||||||
|
"dateModified": "2026-07-24T00:00:00+00:00",
|
||||||
|
"breadcrumb": {
|
||||||
|
"@id": "https://example.com/page/#breadcrumb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
"@id": "https://example.com/page/#breadcrumb",
|
||||||
|
"itemListElement": [
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 1,
|
||||||
|
"name": "Accueil",
|
||||||
|
"item": "https://example.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 2,
|
||||||
|
"name": "Catégorie",
|
||||||
|
"item": "https://example.com/categorie"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "ListItem",
|
||||||
|
"position": 3,
|
||||||
|
"name": "Page actuelle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 FAQPage Schema (critique pour AI Overview)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "FAQPage",
|
||||||
|
"mainEntity": [
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
"name": "Question exacte en langage naturel ?",
|
||||||
|
"acceptedAnswer": {
|
||||||
|
"@type": "Answer",
|
||||||
|
"text": "<p>Réponse concise en 40-60 mots. <strong>Première phrase = réponse directe.</strong> Suite = contexte ou nuance.</p>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Règle critique :** Chaque FAQ doit correspondre à une section H2/H3 visible sur la page. Le texte du schema doit être **identique** au texte visible (pas de contenu caché).
|
||||||
|
|
||||||
|
### 2.4 Speakable Schema (pour assistants vocaux)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Article",
|
||||||
|
"speakable": {
|
||||||
|
"@type": "SpeakableSpecification",
|
||||||
|
"cssSelector": [".summary", ".key-facts"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Stratégie de contenu — Format AI-Overview Ready
|
||||||
|
|
||||||
|
### 3.1 Structure de page type
|
||||||
|
|
||||||
|
```
|
||||||
|
[Zone A — Résumé immédiat]
|
||||||
|
├─ H1 : Question principale / promesse
|
||||||
|
├─ Paragraphe 1 (50-80 mots) : Réponse directe, sans préambule
|
||||||
|
├─ Liste à puces (3-5 items) : Points clés
|
||||||
|
└─ [Optionnel] Encadré "En résumé" ou "Points essentiels"
|
||||||
|
|
||||||
|
[Zone B — Développement]
|
||||||
|
├─ H2 : "Qu'est-ce que [sujet] ?"
|
||||||
|
├─ H2 : "Pourquoi [sujet] est important ?"
|
||||||
|
├─ H2 : "Comment [action] ?" (ou "Quelles sont les étapes ?")
|
||||||
|
├─ H2 : "Quels sont les avantages/inconvénients ?"
|
||||||
|
├─ H2 : "FAQ" (ou "Questions fréquentes")
|
||||||
|
└─ H2 : "Conclusion" ou "Pour aller plus loin"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Règles de rédaction machine-first
|
||||||
|
|
||||||
|
| Règle | Explication | Exemple |
|
||||||
|
|-------|-------------|---------|
|
||||||
|
| **Réponse dès la 1ère phrase** | Pas d'introduction narrative | ❌ "Depuis des siècles, l'humanité..." → ✅ "Le lambi est un mollusque gastéropode des Caraïbes, symbole du patrimoine guadeloupéen." |
|
||||||
|
| **Phrases < 25 mots** | Facilite le parsing par l'IA | Découper les phrases complexes |
|
||||||
|
| **Paragraphes < 100 mots** | Blocs auto-contenus | 3-4 phrases max par `<p>` |
|
||||||
|
| **Vocabulaire consistant** | Même terme pour même concept | Ne pas alterner "lambi" / "conque" / "coquillage" sans explication |
|
||||||
|
| **Active voice** | Sujet + verbe + complément | ❌ "Il est recommandé que..." → ✅ "Nous recommandons..." |
|
||||||
|
| **Pas de jargon sans définition** | Ou définir immédiatement | "Le fédivers (réseaux sociaux décentralisés via ActivityPub)..." |
|
||||||
|
| **Dates visibles et structurées** | `<time datetime="...">` | Mise à jour = crédibilité |
|
||||||
|
| **Chiffres et données** | L'IA cite les chiffres | "317% plus de citations" plutôt que "beaucoup plus" |
|
||||||
|
|
||||||
|
### 3.3 Format spécifique par type de contenu
|
||||||
|
|
||||||
|
**Article informatif :**
|
||||||
|
- Résumé en 3 bullets en tête
|
||||||
|
- H2 sous forme de questions
|
||||||
|
- Réponse en 2-3 phrases sous chaque H2
|
||||||
|
- Source/citation en fin de section
|
||||||
|
|
||||||
|
**Page produit/service :**
|
||||||
|
- "En 1 minute" : ce que c'est, pour qui, prix
|
||||||
|
- Tableau comparatif (si comparaison)
|
||||||
|
- Avantages en liste numérotée
|
||||||
|
- Témoignage avec `Review` schema
|
||||||
|
|
||||||
|
**Page organisation (AKILPA-type) :**
|
||||||
|
- Mission en 1 phrase
|
||||||
|
- 3 piliers d'action en H3
|
||||||
|
- Chiffres clés (adhérents, actions, dates)
|
||||||
|
- Équipe avec photos et bios
|
||||||
|
- Partenaires et réseaux
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Métadonnées et signaux de confiance (EEAT)
|
||||||
|
|
||||||
|
### 4.1 EEAT — Experience, Expertise, Authoritativeness, Trustworthiness
|
||||||
|
|
||||||
|
| Signal | Implémentation technique |
|
||||||
|
|--------|--------------------------|
|
||||||
|
| **Auteur identifié** | Page `/auteurs/[nom]` avec bio, photo, credentials, liens sociaux |
|
||||||
|
| **Date de publication** | `<time datetime="...">` visible + schema `datePublished` |
|
||||||
|
| **Date de modification** | `<time datetime="...">` visible + schema `dateModified` |
|
||||||
|
| **Sources citées** | Liens externes vers sources fiables (`.gov`, `.edu`, médias établis) |
|
||||||
|
| **Mentions légales** | Page `/mentions-legales` avec schema `AboutPage` |
|
||||||
|
| **Politique de confidentialité** | Page `/politique-confidentialite` |
|
||||||
|
| **Contact vérifiable** | Adresse physique, téléphone, email (schema `ContactPoint`) |
|
||||||
|
| **HTTPS obligatoire** | Certificat valide, HSTS activé |
|
||||||
|
| **Mentions d'erreur corrigées** | Section "Mise à jour" ou "Erratum" visible |
|
||||||
|
|
||||||
|
### 4.2 Meta tags essentiels
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Basiques -->
|
||||||
|
<title>Titre de page | Nom du site</title>
|
||||||
|
<meta name="description" content="Résumé de 150-160 caractères. Contient le mot-clé principal. Réponse à la question principale.">
|
||||||
|
<meta name="author" content="Nom Prénom">
|
||||||
|
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
|
||||||
|
|
||||||
|
<!-- Open Graph -->
|
||||||
|
<meta property="og:title" content="Titre de page">
|
||||||
|
<meta property="og:description" content="Description">
|
||||||
|
<meta property="og:type" content="article">
|
||||||
|
<meta property="og:url" content="https://example.com/page">
|
||||||
|
<meta property="og:image" content="https://example.com/image-1200x630.jpg">
|
||||||
|
<meta property="og:image:width" content="1200">
|
||||||
|
<meta property="og:image:height" content="630">
|
||||||
|
<meta property="og:locale" content="fr_FR">
|
||||||
|
<meta property="og:site_name" content="Nom du site">
|
||||||
|
<meta property="article:published_time" content="2026-07-24T00:00:00+00:00">
|
||||||
|
<meta property="article:modified_time" content="2026-07-24T00:00:00+00:00">
|
||||||
|
<meta property="article:author" content="https://example.com/auteurs/nom">
|
||||||
|
<meta property="article:section" content="Catégorie">
|
||||||
|
<meta property="article:tag" content="tag1, tag2, tag3">
|
||||||
|
|
||||||
|
<!-- Twitter Cards -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content="Titre">
|
||||||
|
<meta name="twitter:description" content="Description">
|
||||||
|
<meta name="twitter:image" content="https://example.com/image-1200x600.jpg">
|
||||||
|
<meta name="twitter:creator" content="@compte">
|
||||||
|
|
||||||
|
<!-- Canonical -->
|
||||||
|
<link rel="canonical" href="https://example.com/page">
|
||||||
|
|
||||||
|
<!-- Alternate languages -->
|
||||||
|
<link rel="alternate" hreflang="fr" href="https://example.com/page">
|
||||||
|
<link rel="alternate" hreflang="en" href="https://example.com/en/page">
|
||||||
|
<link rel="alternate" hreflang="x-default" href="https://example.com/page">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Fichier humans.txt (transparence)
|
||||||
|
|
||||||
|
```
|
||||||
|
/* TEAM */
|
||||||
|
Lead Developer: [Nom]
|
||||||
|
Site: [URL]
|
||||||
|
Twitter: [@compte]
|
||||||
|
Location: [Ville, Pays]
|
||||||
|
|
||||||
|
/* THANKS */
|
||||||
|
Name: [Nom] — [Rôle]
|
||||||
|
|
||||||
|
/* SITE */
|
||||||
|
Last update: 2026/07/24
|
||||||
|
Standards: HTML5, CSS3, ES2022
|
||||||
|
Components: [Framework]
|
||||||
|
Software: [Éditeur, outils]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Performance et Core Web Vitals
|
||||||
|
|
||||||
|
### 5.1 Seuils à atteindre
|
||||||
|
|
||||||
|
| Métrique | Objectif | Outil de test |
|
||||||
|
|----------|----------|---------------|
|
||||||
|
| **LCP** (Largest Contentful Paint) | < 2.5s | PageSpeed Insights |
|
||||||
|
| **INP** (Interaction to Next Paint) | < 200ms | PageSpeed Insights |
|
||||||
|
| **CLS** (Cumulative Layout Shift) | < 0.1 | PageSpeed Insights |
|
||||||
|
| **TTFB** (Time to First Byte) | < 600ms | WebPageTest |
|
||||||
|
| **FCP** (First Contentful Paint) | < 1.8s | PageSpeed Insights |
|
||||||
|
|
||||||
|
### 5.2 Checklist technique
|
||||||
|
|
||||||
|
- [ ] **Images :** Format WebP/AVIF, lazy loading (`loading="lazy"`), dimensions explicites (`width`/`height`)
|
||||||
|
- [ ] **Polices :** `font-display: swap`, sous-ensemble de glyphes (subset), préchargement des polices critiques
|
||||||
|
- [ ] **CSS :** Critical CSS inline, reste en fichier externe, pas de `@import`
|
||||||
|
- [ ] **JS :** `defer` ou `async`, pas de render-blocking, code splitting
|
||||||
|
- [ ] **CDN :** Contenu statique servi depuis CDN géolocalisé
|
||||||
|
- [ ] **Compression :** Brotli ou Gzip activé
|
||||||
|
- [ ] **Caching :** Headers `Cache-Control` appropriés
|
||||||
|
- [ ] **HTTP/2 ou HTTP/3 :** Serveur compatible
|
||||||
|
- [ ] **Préconnexion :** `<link rel="preconnect">` vers domaines tiers
|
||||||
|
- [ ] **Prefetching :** `<link rel="prefetch">` pour pages suivantes probables
|
||||||
|
|
||||||
|
### 5.3 Accessibilité (WCAG 2.1 AA minimum)
|
||||||
|
|
||||||
|
- [ ] Contraste texte/fond ≥ 4.5:1 (normal), ≥ 3:1 (gros texte)
|
||||||
|
- [ ] Texte redimensionnable à 200% sans perte de fonction
|
||||||
|
- [ ] Navigation au clavier complète (tabindex logique)
|
||||||
|
- [ ] Lecteur d'écran : labels sur tous les champs de formulaire
|
||||||
|
- [ ] Images : `alt` descriptif (pas de "image" ou "photo")
|
||||||
|
- [ ] Vidéos : sous-titres, transcription texte
|
||||||
|
- [ ] Focus visible sur tous les éléments interactifs
|
||||||
|
- [ ] Pas de contenu clignotant (> 3Hz interdit)
|
||||||
|
- [ ] Formulaires : messages d'erreur explicites, liés aux champs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Maillage interne et architecture d'information
|
||||||
|
|
||||||
|
### 6.1 Structure URL
|
||||||
|
|
||||||
|
```
|
||||||
|
https://example.com/
|
||||||
|
├── /a-propos/
|
||||||
|
├── /services/
|
||||||
|
│ ├── /services/service-a/
|
||||||
|
│ └── /services/service-b/
|
||||||
|
├── /blog/
|
||||||
|
│ ├── /blog/categorie/
|
||||||
|
│ └── /blog/article-slug-optimise/
|
||||||
|
├── /faq/
|
||||||
|
├── /contact/
|
||||||
|
└── /mentions-legales/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Règles :**
|
||||||
|
- URL en minuscules, sans accents, mots séparés par tirets
|
||||||
|
- Pas de paramètres d'URL pour la navigation (utiliser des routes propres)
|
||||||
|
- Slug = mot-clé principal de la page
|
||||||
|
- Profondeur max : 3 clics depuis l'accueil
|
||||||
|
|
||||||
|
### 6.2 Maillage thématique (Query Fan-Out)
|
||||||
|
|
||||||
|
Pour chaque page principale, créer **au moins 3 pages connexes** qui répondent aux questions suivantes logiques :
|
||||||
|
|
||||||
|
```
|
||||||
|
Page principale : "Qu'est-ce que le lambi ?"
|
||||||
|
→ "Où trouve-t-on le lambi ?" (géographie)
|
||||||
|
→ "Comment prépare-t-on le lambi ?" (culture/cuisine)
|
||||||
|
→ "Le lambi est-il en danger ?" (environnement)
|
||||||
|
→ "Quelle est l'importance du lambi en Guadeloupe ?" (patrimoine)
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque page doit contenir :
|
||||||
|
- 3-5 liens internes vers pages connexes (contextuels, pas "Lire aussi")
|
||||||
|
- 1 lien vers page parente
|
||||||
|
- 1 lien vers page d'accueil (via le logo/breadcrumb)
|
||||||
|
|
||||||
|
### 6.3 Breadcrumb navigation
|
||||||
|
|
||||||
|
```html
|
||||||
|
<nav aria-label="Fil d'Ariane">
|
||||||
|
<ol itemscope itemtype="https://schema.org/BreadcrumbList">
|
||||||
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
||||||
|
<a itemprop="item" href="/">
|
||||||
|
<span itemprop="name">Accueil</span>
|
||||||
|
</a>
|
||||||
|
<meta itemprop="position" content="1">
|
||||||
|
</li>
|
||||||
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
||||||
|
<a itemprop="item" href="/categorie/">
|
||||||
|
<span itemprop="name">Catégorie</span>
|
||||||
|
</a>
|
||||||
|
<meta itemprop="position" content="2">
|
||||||
|
</li>
|
||||||
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
||||||
|
<span itemprop="name">Page actuelle</span>
|
||||||
|
<meta itemprop="position" content="3">
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Multimédia et contenu enrichi
|
||||||
|
|
||||||
|
### 7.1 Images
|
||||||
|
|
||||||
|
| Attribut | Règle |
|
||||||
|
|----------|-------|
|
||||||
|
| `alt` | Description fonctionnelle (pas décorative) |
|
||||||
|
| `width` / `height` | Dimensions réelles pour éviter le CLS |
|
||||||
|
| `loading="lazy"` | Sauf image LCP (héros) |
|
||||||
|
| `decoding="async"` | Pour toutes les images non-LCP |
|
||||||
|
| Format | WebP prioritaire, fallback JPEG/PNG |
|
||||||
|
| Taille | < 200 Ko idéalement, < 500 Ko maximum |
|
||||||
|
| Nom de fichier | `mots-cles-descriptifs.webp` (pas `IMG_1234.jpg`) |
|
||||||
|
|
||||||
|
### 7.2 Vidéos
|
||||||
|
|
||||||
|
- Durée : 60-90s pour les vidéos d'aperçu (format privilégié par AI Overview)
|
||||||
|
- Hébergement : YouTube (SEO) + auto-hébergé WebM/MP4 (souveraineté)
|
||||||
|
- Schema `VideoObject` obligatoire
|
||||||
|
- Transcription texte visible sous la vidéo
|
||||||
|
- Thumbnail optimisé (1280x720 minimum)
|
||||||
|
|
||||||
|
### 7.3 Infographies et données
|
||||||
|
|
||||||
|
- Toujours accompagnées d'un texte alternatif complet
|
||||||
|
- Données en tableau HTML (pas image)
|
||||||
|
- Schema `Dataset` si données structurées
|
||||||
|
- Téléchargement possible (PDF ou CSV)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Robots.txt et gestion des crawlers
|
||||||
|
|
||||||
|
### 8.1 Template standard
|
||||||
|
|
||||||
|
```
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
Disallow: /admin/
|
||||||
|
Disallow: /api/
|
||||||
|
Disallow: /panier/
|
||||||
|
Disallow: /compte/
|
||||||
|
Disallow: /*?*
|
||||||
|
Disallow: /recherche/
|
||||||
|
|
||||||
|
# Blocage crawlers d'entraînement (optionnel)
|
||||||
|
User-agent: GPTBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: CCBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: Google-Extended
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
# Sitemap
|
||||||
|
Sitemap: https://example.com/sitemap.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note critique :** `Google-Extended` bloque l'entraînement de Gemini, mais PAS Google AI Overview (qui utilise Googlebot standard). Il n'existe actuellement aucun moyen technique d'être dans Google Search tout en étant exclu de l'AI Overview.
|
||||||
|
|
||||||
|
### 8.2 Sitemap.xml
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||||
|
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
||||||
|
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
|
||||||
|
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://example.com/page</loc>
|
||||||
|
<lastmod>2026-07-24</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
<image:image>
|
||||||
|
<image:loc>https://example.com/image.webp</image:loc>
|
||||||
|
<image:title>Titre de l'image</image:title>
|
||||||
|
<image:caption>Légende descriptive</image:caption>
|
||||||
|
</image:image>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Internationalisation (si applicable)
|
||||||
|
|
||||||
|
### 9.1 Structure multi-langue
|
||||||
|
|
||||||
|
**Recommandé :** Sous-répertoires avec `hreflang`
|
||||||
|
```
|
||||||
|
/fr/ — Français (par défaut)
|
||||||
|
/en/ — Anglais
|
||||||
|
/ht/ — Créole haïtien
|
||||||
|
/gp/ — Guadeloupe (variante régionale)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 Attributs lang
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html lang="fr">
|
||||||
|
<p lang="ht">Fraz an kreyòl.</p>
|
||||||
|
<p lang="en">Sentence in English.</p>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Checklist de livraison (pour agent LLM)
|
||||||
|
|
||||||
|
### Avant mise en ligne
|
||||||
|
|
||||||
|
- [ ] HTML sémantique validé (W3C Validator)
|
||||||
|
- [ ] Schema.org JSON-LD testé (Google Rich Results Test)
|
||||||
|
- [ ] Core Web Vitals tous verts (PageSpeed Insights)
|
||||||
|
- [ ] Accessibilité WCAG 2.1 AA (axe DevTools ou Lighthouse)
|
||||||
|
- [ ] Mobile-first responsive (Chrome DevTools)
|
||||||
|
- [ ] Meta tags complets et uniques par page
|
||||||
|
- [ ] Canonical et hreflang corrects
|
||||||
|
- [ ] Robots.txt et sitemap.xml fonctionnels
|
||||||
|
- [ ] HTTPS forcé (HSTS)
|
||||||
|
- [ ] Pas de contenu dupliqué (Siteliner ou Screaming Frog)
|
||||||
|
- [ ] Liens internes fonctionnels (pas de 404)
|
||||||
|
- [ ] Images optimisées (format, taille, alt)
|
||||||
|
- [ ] Formulaires testés (validation, messages d'erreur)
|
||||||
|
- [ ] Analytics et Search Console connectés
|
||||||
|
|
||||||
|
### Post-mise en ligne
|
||||||
|
|
||||||
|
- [ ] Soumission sitemap dans Google Search Console
|
||||||
|
- [ ] Inspection d'URL pour les pages clés
|
||||||
|
- [ ] Test Rich Results pour chaque type de schema
|
||||||
|
- [ ] Monitoring Core Web Vitals (Search Console > Experience)
|
||||||
|
- [ ] Indexation vérifiée (`site:example.com`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Anti-patterns à bannir absolument
|
||||||
|
|
||||||
|
| Anti-pattern | Pourquoi c'est mortel | Solution |
|
||||||
|
|--------------|----------------------|----------|
|
||||||
|
| Contenu généré par IA non révisé | Hallucinations, pénalité Google "Helpful Content" | Révision humaine obligatoire, sources citées |
|
||||||
|
| Keyword stuffing | Pénalité algorithmique | Vocabulaire naturel, LSI keywords |
|
||||||
|
| Contenu caché (display:none pour SEO) | Pénalité manuelle | Contenu visible ou schema approprié |
|
||||||
|
| Liens achetés / fermes de liens | Pénalité manuelle | Maillage naturel, guest posting qualité |
|
||||||
|
| Duplicate content interne | Cannibalisation | Canonical, consolidation, 301 |
|
||||||
|
| Pages vides / placeholders | Indexation gaspillée | Noindex jusqu'à contenu complet |
|
||||||
|
| JavaScript pour le contenu principal | Crawler ne voit pas le contenu | SSR ou hydratation côté serveur |
|
||||||
|
| Pop-ups intrusives (interstitiels) | Pénalité mobile | Bannières discrètes, pas de blocage |
|
||||||
|
| URLs avec session ID | Contenu dupliqué | Cookies pour les sessions |
|
||||||
|
| Redirections en chaîne | Perte de jus SEO | Redirection directe 301 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Spécificités contexte Caraïbe / AKILPA
|
||||||
|
|
||||||
|
### 12.1 Référencement local
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@type": "LocalBusiness",
|
||||||
|
"address": {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
"streetAddress": "...",
|
||||||
|
"addressLocality": "Basse-Terre",
|
||||||
|
"addressRegion": "Guadeloupe",
|
||||||
|
"postalCode": "97100",
|
||||||
|
"addressCountry": "FR"
|
||||||
|
},
|
||||||
|
"geo": {
|
||||||
|
"@type": "GeoCoordinates",
|
||||||
|
"latitude": "15.9950",
|
||||||
|
"longitude": "-61.7295"
|
||||||
|
},
|
||||||
|
"telephone": "+590-...",
|
||||||
|
"openingHoursSpecification": [...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 12.2 Contenu patrimonial
|
||||||
|
|
||||||
|
- **Termes en créole :** baliser avec `<span lang="ht">` ou `<span lang="gcf">` (guadeloupéen)
|
||||||
|
- **Définitions :** glossaire lié ou infobulles pour les termes locaux
|
||||||
|
- **Sources :** citation des archives, ouvrages de référence (Fanon, Glissant, etc.)
|
||||||
|
- **Multimédia :** photos d'archives avec crédits, enregistrements audio (voix créole)
|
||||||
|
|
||||||
|
### 12.3 Souveraineté numérique
|
||||||
|
|
||||||
|
- **Auto-hébergement privilégié** (pas de Google Fonts, pas de CDN Google)
|
||||||
|
- **Analytics alternatives** : Matomo, Plausible (pas Google Analytics)
|
||||||
|
- **Polices libres** : Google Fonts self-hostées ou alternatives (Fontsource)
|
||||||
|
- **Pas de reCAPTCHA** : hCaptcha ou solution libre
|
||||||
|
- **Formulaires** : pas de Typeform/Google Forms, solutions auto-hébergées
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Ressources et outils de validation
|
||||||
|
|
||||||
|
| Outil | Usage | URL |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| **W3C Validator** | HTML sémantique | validator.w3.org |
|
||||||
|
| **Google Rich Results Test** | Schema.org | search.google.com/test/rich-results |
|
||||||
|
| **PageSpeed Insights** | Core Web Vitals | pagespeed.web.dev |
|
||||||
|
| **Lighthouse** | Audit complet (Chrome DevTools) | Intégré Chrome |
|
||||||
|
| **Schema Markup Validator** | Schema.org | validator.schema.org |
|
||||||
|
| **axe DevTools** | Accessibilité | deque.com/axe/devtools |
|
||||||
|
| **Screaming Frog** | Crawl SEO (desktop) | screamingfrog.co.uk |
|
||||||
|
| **Siteliner** | Contenu dupliqué | siteliner.com |
|
||||||
|
| **Google Search Console** | Monitoring indexation | search.google.com/search-console |
|
||||||
|
| **Bing Webmaster Tools** | Indexation Bing | bing.com/webmasters |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Glossaire des termes techniques
|
||||||
|
|
||||||
|
| Terme | Définition |
|
||||||
|
|-------|------------|
|
||||||
|
| **AI Overview** | Résumé généré par IA en haut des résultats Google |
|
||||||
|
| **Schema.org** | Vocabulaire sémantique structuré pour le web |
|
||||||
|
| **JSON-LD** | Format de données liées pour Schema.org |
|
||||||
|
| **EEAT** | Experience, Expertise, Authoritativeness, Trustworthiness |
|
||||||
|
| **LCP** | Largest Contentful Paint (métrique de performance) |
|
||||||
|
| **INP** | Interaction to Next Paint (métrique d'interactivité) |
|
||||||
|
| **CLS** | Cumulative Layout Shift (métrique de stabilité visuelle) |
|
||||||
|
| **Hreflang** | Attribut indiquant la langue/cible géo d'une page |
|
||||||
|
| **Canonical** | URL préférée pour éviter le contenu dupliqué |
|
||||||
|
| **Sitemap** | Fichier listant les URLs à indexer |
|
||||||
|
| **Robots.txt** | Instructions pour les crawlers |
|
||||||
|
| **SSR** | Server-Side Rendering (rendu côté serveur) |
|
||||||
|
| **CSR** | Client-Side Rendering (rendu côté client) |
|
||||||
|
| **Lazy loading** | Chargement différé des ressources |
|
||||||
|
| **Critical CSS** | CSS nécessaire au rendu initial, inline |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Document généré pour une utilisation par agents LLM dans le cadre de projets web.
|
||||||
|
À adapter selon le contexte spécifique de chaque site.*
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
|||||||
|
# CHARTE OKI — DESIGN SYSTEM & HARMONISATION MULTI-PROJETS
|
||||||
|
### ORGANISATION KA INTERNATIONALE · appliquer la recette SvelteKit avec la marque OKI
|
||||||
|
|
||||||
|
> **Nature de ce document :** référence de marque opérationnelle, destinée aux humains et aux agents de code. Il consolide le `BRAND.md` existant (valeurs mesurées sur o-k-i.net, juillet 2026 — **source canonique** pour couleurs, typo, voix) et y ajoute les tokens prêts à l'emploi, les règles de motion, l'iconographie SVG, et la matrice d'application projet par projet. **En cas de conflit : ce document prime sur la recette générique pour tout ce qui touche à la marque ; la recette prime pour tout ce qui touche à la méthode technique.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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**. La marque est afrofuturiste caribéenne : un drapeau, pas une charte corporate.
|
||||||
|
|
||||||
|
Trois conséquences non négociables :
|
||||||
|
1. **Le thème sombre est l'identité par défaut** ; le clair est une variante opt-in (`html.light-theme`) qui assombrit les accents pour rester WCAG AA.
|
||||||
|
2. **L'or porte toute l'interaction** — c'est la seule couleur d'action.
|
||||||
|
3. **Angles nets partout** — aucune forme organique, aucune bulle très arrondie.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. TOKENS — PRÊTS À L'EMPLOI
|
||||||
|
|
||||||
|
### 2.1 Couleurs (mesurées sur le site réel)
|
||||||
|
|
||||||
|
```css
|
||||||
|
: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 : décorations et sémantique) ── */
|
||||||
|
--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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Règles d'usage (mesurées, à faire respecter) :
|
||||||
|
- Texte secondaire = blanc crème à 70–85 % d'opacité, **pas** une couleur dédiée.
|
||||||
|
- Filets = blanc à 10 %, jamais de gris plein.
|
||||||
|
- Rouge = signaler · Vert = valider · Or = agir. Ne jamais permuter ces rôles.
|
||||||
|
|
||||||
|
### 2.2 Typographie
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--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`. Graisses minimales : 400 body / 600 display (lisibilité écrans bas de gamme).
|
||||||
|
- **Self-hébergement obligatoire** : woff2 dans `fonts/` + `fonts/fonts.css`, `font-display: swap`. Aucun appel Google Fonts (contradiction avec la doctrine souveraineté — bug en cours de correction sur o-k-i.net).
|
||||||
|
|
||||||
|
### 2.3 Formes, espacements, layout
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--radius-sm: 3px; /* boutons, tags, badges */
|
||||||
|
--radius-md: 6px; /* cartes — jamais plus */
|
||||||
|
--border-card: 1px solid var(--line);
|
||||||
|
--border-btn: 2px solid var(--or-oki);
|
||||||
|
--space-1: 0.75rem; --space-2: 1rem; --space-3: 1.5rem; --space-4: 2rem; --space-5: 4rem;
|
||||||
|
--container: 1200px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Sections : `padding: var(--space-5) var(--space-4)` · Grille : `repeat(auto-fit, minmax(300px, 1fr))`.
|
||||||
|
- **Carte OKI canonique** : fond blanc 3 %, liseré gauche 4 px d'accent (vert ou or), survol `translateY(-2px à -4px)` + bordure d'accent.
|
||||||
|
- **Tag OKI canonique** : fond or 8 % + bordure or 1 px, rayon 3 px.
|
||||||
|
|
||||||
|
### 2.4 La 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é**.
|
||||||
|
|
||||||
|
```css
|
||||||
|
.flag-bar {
|
||||||
|
height: 6px;
|
||||||
|
background: linear-gradient(to right,
|
||||||
|
var(--noir-oki) 0 25%, var(--or-oki) 25% 50%,
|
||||||
|
var(--vert-oki) 50% 75%, var(--rouge-oki) 75% 100%);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Placement : bord inférieur de la navigation fixe ; peut ponctuer un héros ou un pied de page. **Une occurrence visible par écran maximum.** C'est l'élément le plus reconnaissable de la marque — le diluer, c'est le tuer.
|
||||||
|
|
||||||
|
### 2.5 Logotype
|
||||||
|
|
||||||
|
Monogramme « OKI » : arche or, point vert, accent rouge sur noir. Fichier canonique `logos/favicon-1.png` (512×512, transparent — aussi og:image et apple-touch-icon) ; déclinaisons `logos/favicon-0.ico`, `assets/maskable_oki.png`. Les `logos/header-img-*` sont des marques de **partenaires** (Joukawouvè, Aktivist Vybz, cyber-mawonaj, KA UBUNTU) : provenance conservée, **ne jamais les utiliser comme logo OKI**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. MOTION OKI — LES CADENCES GWOKA
|
||||||
|
|
||||||
|
La marque ne bouge pas comme une startup : elle pulse comme un ka. Tokens obligatoires dans tout projet OKI animé :
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--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 */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Règles de motion :
|
||||||
|
1. **Le stagger de marque OKI est syncopé (3+3+2)**, pas linéaire : délais `[0, 120, 300, 360, 600, 660, 900, 960]` ms pour 8 éléments. C'est subtil — c'est le but : le rythme est une identité, pas un effet.
|
||||||
|
2. Uniquement `transform` + `opacity` (recette P3/budgets).
|
||||||
|
3. `prefers-reduced-motion` : gate unique au provider, contenu statique complet (le site actuel n'a **aucune** garde — à corriger partout).
|
||||||
|
4. Durées toujours lues depuis les tokens, jamais hardcodées.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. ICONOGRAPHIE — LA RÈGLE « ZÉRO EMOJI EN PRODUCTION »
|
||||||
|
|
||||||
|
Le site actuel utilise des emojis (✊🏿🔓🤝🏿🛡️) : rendu dépendant de l'OS, incohérent avec la précision du drapeau. **Tout projet harmonisé remplace les emojis par le set SVG OKI.**
|
||||||
|
|
||||||
|
Spécifications du set :
|
||||||
|
- ViewBox 24×24, **stroke 2 px**, angles nets (pas de courbes organiques), une seule couleur : `currentColor` (or par défaut sur noir).
|
||||||
|
- Remplissage réservé aux pictogrammes ≤ 16 px.
|
||||||
|
- Grille de nommage en KA : les fichiers portent des noms kréyòl.
|
||||||
|
|
||||||
|
Set initial à produire (correspondances avec l'existant) :
|
||||||
|
|
||||||
|
| Fichier | Sujet | Remplace / usage |
|
||||||
|
|---|---|---|
|
||||||
|
| `ka.svg` | tambour ka | icône maîtresse, 404, loader |
|
||||||
|
| `lambi.svg` | conque lambi | communication, annonces, « bòkaz » |
|
||||||
|
| `zetwal.svg` | étoile 4 branches | navigation, favoris, instances fédivers |
|
||||||
|
| `mawon.svg` | silhouette/brisure de chaîne stylisée | souveraineté, valeurs |
|
||||||
|
| `lakanmou.svg` | flamme | engagement, dons ponctuels |
|
||||||
|
| `jaden.svg` | pousse/jardin | solidarité, lakou, projets |
|
||||||
|
| `kannen.svg` | canne à sucre stylisée | patrimoine, histoire |
|
||||||
|
| `lanme.svg` | vague | océan, liens internationaux, Réunion↔Gwada |
|
||||||
|
| `glo.svg` | poing levé géométrique | ✊🏿 — luttes, tarifs solidaires |
|
||||||
|
| `kle.svg` | clé/cadenas ouvert | 🔓 — liberté, open-source |
|
||||||
|
| `pawol.svg` | bulle de parole angulaire | langues, traduction, PAWÒL-NU |
|
||||||
|
| `mizik-note.svg` | note de musique | MIZIK, DJANGOKAM |
|
||||||
|
|
||||||
|
Chaque picto livré en `<symbol>` dans un sprite `icons.svg` + usage `<svg><use href="icons.svg#ka"/></svg>` (une seule requête, stylable en CSS).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. VOIX & TON (canonique, reprise du BRAND.md)
|
||||||
|
|
||||||
|
Vouvoiement direct et empathique, phrases courtes. On parle d'abord de la cause du visiteur — « Vous luttez, vous organisez, vous mobilisez » — puis de l'outil. La technique sert la lutte, jamais l'inverse.
|
||||||
|
|
||||||
|
- **À dire :** sur mesure · tarifs adaptés à vos moyens · organisations engagées · transition numérique · écosystème numérique libre · à la hauteur.
|
||||||
|
- **À éviter :** « template générique », jargon corporate/marketing, « clé en main », startup-speak.
|
||||||
|
- Piliers : ORGANISATION KA · INTERNATIONALE · Des solutions pour chaque besoin · Le budget ne doit jamais être un frein.
|
||||||
|
- **Langues :** FR par défaut ; le KA (kréyòl) en signature, titres de sections symboliques et microcopy (404, loader, remerciements) ; EN en version dédiée. Attribut `lang` correct à chaque bascule.
|
||||||
|
|
||||||
|
## 6. IMAGERIE (canonique)
|
||||||
|
|
||||||
|
Illustration numérique afrofuturiste caribéenne : scènes au coucher de soleil, personnages caribéens, pochettes musicales typographiées. Couleurs chaudes saturées sur fonds sombres. **Jamais** de photo de banque d'images corporate, jamais d'illustration générique de startup, jamais d'icône ou de logo en guise d'image. Formats : AVIF/WebP, self-hébergés, `loading="lazy"` hors LCP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. MATRICE D'APPLICATION PAR PROJET
|
||||||
|
|
||||||
|
Harmoniser ≠ tout refaire : chaque plateforme a un canal de personnalisation officiel. N'UTILISER QUE ces canaux (jamais de fork pour du style).
|
||||||
|
|
||||||
|
| Projet | Logiciel | Canal de theming | Application OKI |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **o-k-i.net** | SvelteKit (migration en cours) | Natif | Référence absolue : tokens §2, flag-bar, village SVG, KineticText gwoka |
|
||||||
|
| **BOKANTE** | Mastodon | CSS personnalisé admin + `custom.css` | Variables Mastodon remappées sur tokens OKI : fond `--noir-oki`, accent `--or-oki`, boutons Archivo uppercase ; logo colonne = monogramme OKI ; flag-bar en haut de la colonne de composition |
|
||||||
|
| **GADE** | PeerTube | Thème CSS admin (champ « CSS personnalisé ») | Mêmes remappages ; player : contrôles or sur noir ; page À-propos = texte marque (« vidéos sans algorithme, dédiées aux cultures afro-diasporiques ») |
|
||||||
|
| **NIYAJ** | Nextcloud | App **Theming** officielle | Couleur primaire `#FDB813`, fond sombre, logo + slogan « Votre cloud, vos données » ; favicon OKI |
|
||||||
|
| **MIZIK** | Funkwhale | `custom.css` admin | Fond `--noir-oki`, accents or, player accentué or ; typographie : Inter si surchargeable, sinon système |
|
||||||
|
| **KUTE** | App PHP maison | Natif | Appliquer les tokens §2 en entier — c'est un projet first-party, aucune excuse de ne pas être au niveau d'o-k-i.net |
|
||||||
|
| **LABOLA** | Forgejo/Gitea | `custom/templates` + `custom/public/css` | Thème sombre OKI, accent or ; logo forge = monogramme ; **ne pas toucher aux templates Go au-delà du header/footer** (fragile aux upgrades) |
|
||||||
|
| **PAWÒL-NU / djangokam.pawol.nu / nyyoka.pawol.nu** | Selon stack | Natif | Charte complète + liberté artistique accrue (pochettes, kinetic type poussé) : c'est la vitrine culturelle, le motion peut y être plus riche qu'ailleurs |
|
||||||
|
| **fediverse.o-k-i.net** | Statique | Natif | **Constellation zétwal** : chaque instance = étoile SVG reliée, hover/focus = éclat or + label ; DOM/SVG léger uniquement |
|
||||||
|
|
||||||
|
### Le footer fédéré commun (tous les sous-domaines)
|
||||||
|
|
||||||
|
```
|
||||||
|
[monogramme OKI] Un service libre opéré par ORGANISATION KA INTERNATIONALE · o-k-i.net
|
||||||
|
```
|
||||||
|
+ flag-bar 6 px au-dessus du footer. Une seule ligne, `--muted`, lien or. C'est le fil rouge qui fait l'écosystème.
|
||||||
|
|
||||||
|
### Règles cross-projets
|
||||||
|
|
||||||
|
1. **Un domaine, une famille** : `*.o-k-i.net` = écosystème fédivers et outils ; `pawol.nu` = projets culturels/musicaux. Ne pas mélanger.
|
||||||
|
2. Chaque service porte un **nom KA + le nom du logiciel en sous-titre** (pattern existant : « BOKANTE — Mastodon », « NIYAJ — Nextcloud »). Ne jamais masquer le logiciel sous-jacent : la transparence est un pilier.
|
||||||
|
3. Favicon = monogramme OKI partout (déclinaisons ico/png/maskable).
|
||||||
|
4. og:image par service : monogramme sur `--noir-oki` + nom du service en Archivo or.
|
||||||
|
5. Toute nouvelle page statique du réseau démarre du template o-k-i.net (tokens inclus), jamais de zéro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. CHECKLIST D'HARMONISATION D'UN PROJET (DoD marque)
|
||||||
|
|
||||||
|
- [ ] Thème sombre par défaut, clair opt-in contrasté AA.
|
||||||
|
- [ ] Tokens §2 importés (pas de valeur recopiée à la main — un fichier `oki-tokens.css` partagé).
|
||||||
|
- [ ] Or = seule couleur d'action ; rouge/vert dans leurs rôles sémantiques.
|
||||||
|
- [ ] Archivo/Inter self-hébergées, zéro fonte externe.
|
||||||
|
- [ ] Flag-bar présente, segments francs, ≤ 1 par écran.
|
||||||
|
- [ ] Zéro emoji en interface (set SVG §4).
|
||||||
|
- [ ] Tokens motion §3 + gate `prefers-reduced-motion`.
|
||||||
|
- [ ] Footer fédéré + monogramme + favicon OKI.
|
||||||
|
- [ ] Voix conforme §5 (test : aucune occurrence de « clé en main », « solution innovante », « disruptive »).
|
||||||
|
- [ ] Zéro requête tierce au chargement (fonts, CDN, analytics) — vérifié onglet réseau.
|
||||||
|
- [ ] Nom KA + logiciel en sous-titre visible en page d'accueil du service.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
@@ -0,0 +1,494 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
|
```css
|
||||||
|
: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
|
||||||
|
|
||||||
|
```css
|
||||||
|
--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
|
||||||
|
|
||||||
|
```css
|
||||||
|
--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**.
|
||||||
|
|
||||||
|
```css
|
||||||
|
.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
|
||||||
|
|
||||||
|
```css
|
||||||
|
--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é —
|
||||||
|
```bash
|
||||||
|
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|peertube|nextcloud|gitea|castopod|discord|telegram|whatsapp|email|stoat|tiktok|x" />` |
|
||||||
|
| 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)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 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.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# PROMPT KIMI CLI — Transformation de o-k-i.net en SvelteKit
|
||||||
|
|
||||||
|
> **Mode d'emploi :** copie l'intégralité du bloc ci-dessous (entre les lignes `---`) dans Kimi CLI, en joignant les deux documents de référence : `recette-sveltekit-playbook-agent.md` (la méthode) et `charte-oki-design-system.md` (la marque). Ajuste la section « Périmètre de cette session » selon ce que tu veux faire en une passe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# MISSION : Migrer o-k-i.net vers SvelteKit selon la recette et la charte OKI
|
||||||
|
|
||||||
|
Tu es un agent de code senior. Ta mission : reconstruire le site **o-k-i.net** (ORGANISATION KA INTERNATIONALE) en SvelteKit, en suivant **à la lettre** le playbook `recette-sveltekit-playbook-agent.md` (méthode, budgets, DoD) et la charte `charte-oki-design-system.md` (couleurs, typo, voix, iconographie — elle prime pour tout ce qui est marque).
|
||||||
|
|
||||||
|
## 1. Contexte du site existant (audit réel, juillet 2026)
|
||||||
|
|
||||||
|
**Identité :** association tech militante guadeloupéenne — « Le numérique au service de vos luttes ». Applis web sur mesure à tarifs solidaires pour associations, collectifs et organisations militantes. Positionnement souveraineté numérique : indépendance des GAFAM, logiciels libres, hébergement éthique. L'identité visuelle est afrofuturiste caribéenne : drapeau panafricain (noir/or/vert/rouge), **thème sombre par défaut**.
|
||||||
|
|
||||||
|
**Écosystème lié (liens externes à préserver, ne pas migrer) :**
|
||||||
|
- bokante.o-k-i.net → instance Mastodon
|
||||||
|
- gade.o-k-i.net → instance PeerTube
|
||||||
|
- mizik.o-k-i.net → instance Funkwhale
|
||||||
|
- niyaj.o-k-i.net → Nextcloud
|
||||||
|
- kute.o-k-i.net → application PHP maison
|
||||||
|
- labola.o-k-i.net → forge (code source public, dont celui du site)
|
||||||
|
- fediverse.o-k-i.net → portail fédivers
|
||||||
|
- pawol.nu + djangokam.pawol.nu + nyyoka.pawol.nu → paroles/traductions/karaokés (projet DJANGOKAM, artiste IA caribéen)
|
||||||
|
|
||||||
|
**Stack actuelle mesurée :** HTML statique + CSS vanilla (~44 KB) + 3 petits JS vanilla (main.js, theme.js, lang-redirect.js). Hébergement o2switch (mutualisé FR). Images via BunnyCDN (`organisationka.b-cdn.net`). Headers de sécurité exemplaires à **conserver à l'identique ou renforcer** : CSP `default-src 'self'` (+ style 'unsafe-inline' + fonts.googleapis.com), HSTS, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy` restrictive.
|
||||||
|
|
||||||
|
**Bugs et défauts de production confirmés (à corriger dans la migration) :**
|
||||||
|
1. **CRITIQUE — Toutes les images renvoient 403** (pull zone BunnyCDN cassée) : logo header, logos partenaires, portrait DJANGOKAM, pochettes, logos clients, og:image. Décision cible : **self-héberger toutes les images** dans `static/images/` (AVIF/WebP générés au build via vite-imagetools), supprimer toute dépendance à BunnyCDN, retirer `https://organisationka.b-cdn.net` de la CSP.
|
||||||
|
2. **Google Fonts** chargé (fonts.googleapis.com + gstatic.com) alors que le site revendique l'indépendance des GAFAM → self-héberger Archivo (600-900) et Inter (400-800) en woff2, retirer les deux domaines de la CSP.
|
||||||
|
3. Aucun `prefers-reduced-motion` nulle part.
|
||||||
|
4. Iconographie 100 % emoji (✊🏿🔓🤝🏿🛡️💼🏛️) → set SVG conforme à la charte (angles nets, stroke 2 px, or sur noir).
|
||||||
|
5. Les 3 cards « publics » (Militants / Associations & Collectifs / Entreprises) apparaissent en double dans la page → une seule occurrence.
|
||||||
|
6. Lazy-loading d'images partiel (4/~14) → systématique hors LCP.
|
||||||
|
|
||||||
|
**Contenu éditorial (conserver tel quel, sauf mention contraire) :** one-page FR à ancres — Hero (« ORGANISATION KA INTERNATIONALE », 3 badges, 2 CTA) · Solutions ×3 publics · Projets web · Partenaires (Joukawouvè, Aktivist Vybz #AKV, cyber-mawonaj) · DJANGOKAM (artiste IA caribéen, lore Wanakaera/Karukera, liens pawol.nu, plateformes musicales, presse) · Mission + 4 valeurs (Engagement, Liberté, Solidarité, Souveraineté) · Développement web sur mesure · Clients (KA UBUNTU kaubuntu.com, KA UBUNTU Hub kaubuntu.re PWA, KBM kbm.gp) · Services hébergés (BOKANTE, GADE, NIYAJ, etc.) · FAQ « Qu'est-ce que le Ka ? » · CTA final + contact (kontak@o-k-i.net, WhatsApp, Telegram, Discord, forge) · footer avec mentions légales. Pages satellites : `/dons/` (Stripe + Liberapay), `/en/` (version anglaise complète), `/mentions-legales/`.
|
||||||
|
|
||||||
|
## 2. Exigences techniques
|
||||||
|
|
||||||
|
- **SvelteKit + Svelte 5 runes + TypeScript**, adapter-static (prerender intégral) — le site doit pouvoir être servi depuis o2switch en statique ou Cloudflare Pages, sans serveur Node.
|
||||||
|
- **i18n FR/EN** : FR par défaut, EN sur `/en/` comme actuellement ; attributs `lang` corrects ; pas de lib lourde si un routage par dossier suffit.
|
||||||
|
- **Thème sombre par défaut** (identité de marque), thème clair en opt-in persisté en localStorage, appliqué sans FOUC via snippet dans `app.html`.
|
||||||
|
- **Conserver toutes les URLs** existantes (/, /dons/, /en/, /mentions-legales/, ancres #solutions #projets #partenaires #djangokam #hebergement #contact).
|
||||||
|
- **Headers de sécurité** : reproduire la CSP et les headers actuels (via `_headers` Cloudflare ou doc d'htaccess o2switch), en retirant les domaines devenus inutiles (Google Fonts, BunnyCDN).
|
||||||
|
- **PWA légère** : manifest + service worker de cache du shell (vite-plugin-pwa) — le site doit rester lisible hors-ligne.
|
||||||
|
- OpenGraph complet (10 balises actuelles) reconduit, og:image self-hébergée.
|
||||||
|
- Budgets du playbook §4 : JS initial ≤ 170 KB, page d'accueil ≤ 2 MB, Lighthouse mobile ≥ 90/95/95/95.
|
||||||
|
|
||||||
|
## 3. Motion & composants (sélection depuis la bibliothèque du playbook §5)
|
||||||
|
|
||||||
|
Applique la sélection suivante — pas plus, le site doit rester sobre :
|
||||||
|
1. **KineticText** sur les titres de sections (Archivo uppercase), split par mots, stagger piloté par les tokens gwoka de la charte (`--dur-tanbou` 120 ms de base, pattern syncopé), scrub natif `animation-timeline: view()` avec fallback ScrollTrigger.
|
||||||
|
2. **ScrollProgressBar** en haut de page, gradient or→vert de la charte.
|
||||||
|
3. **PageTransition** légère (View Transitions + fallback), durée `--dur-mesure` 480 ms.
|
||||||
|
4. **Village écosystème** : remplacer la grille de cards « Services hébergés » par une scène SVG isométrique « village créole » (case à ka = BOKANTE, karbay = GADE, silo nuage = NIYAJ, scène de musique = MIZIK…), chaque bâtiment = lien vers l'instance, hover/focus = liseré or + label. **DOM/SVG uniquement, aucun WebGL** (P3 du playbook). Fallback : grille de cards accessible pour reduced-motion/mobile étroit.
|
||||||
|
5. **Flag-bar** (signature marque, 4 segments francs 6 px) : sous la nav fixe + en haut du footer, jamais plus d'une occurrence visible par écran.
|
||||||
|
6. **404 designé** en KA (texte créole + ka/tambour SVG), page hors-ligne PWA assortie.
|
||||||
|
7. CursorTracker : **ne pas installer** (audience mobile-dominante, ROI faible).
|
||||||
|
8. WebGL : **aucun** sur cette migration.
|
||||||
|
|
||||||
|
## 4. Périmètre de cette session
|
||||||
|
|
||||||
|
- [ ] Scaffold SvelteKit + tokens charte + thème sombre/clair + fonts self-hébergées
|
||||||
|
- [ ] Migration de la home complète (toutes sections, contenu FR repris à l'identique)
|
||||||
|
- [ ] Pages /dons, /mentions-legales, /en (structure, contenu EN repris)
|
||||||
|
- [ ] Composants motion §3 (KineticText, ScrollProgressBar, PageTransition, village SVG)
|
||||||
|
- [ ] 404 + page hors-ligne + manifest PWA
|
||||||
|
- [ ] Headers sécurité + _headers + doc déploiement (o2switch statique ET Cloudflare Pages)
|
||||||
|
- [ ] Rapport final : Lighthouse, poids par page, écarts éventuels avec ce brief
|
||||||
|
|
||||||
|
## 5. Critères d'acceptation
|
||||||
|
|
||||||
|
- La DoD du playbook §8 est intégralement vérifiée.
|
||||||
|
- Zéro requête vers un domaine tiers au chargement (vérifier l'onglet réseau) : ni Google, ni BunnyCDN, ni aucun CDN.
|
||||||
|
- Toutes les images présentes et optimisées (plus aucun 403, plus aucune image > 200 KB hors hero).
|
||||||
|
- Le contenu éditorial FR est repris à l'identique (aucune réécriture non demandée), hormis la déduplication des cards « publics ».
|
||||||
|
- La nav au clavier fonctionne sur tout le village SVG (chaque bâtiment focusable, label lisible).
|
||||||
|
- `npm run build && npm run preview` : zéro erreur, zéro warning a11y.
|
||||||
|
|
||||||
|
Commence par la Phase 1 du playbook (audit de confirmation du dépôt actuel) et présente-moi le plan avant d'écrire le moindre composant.
|
||||||
@@ -0,0 +1,908 @@
|
|||||||
|
# 15 Award Sites → The SvelteKit Recipe
|
||||||
|
### An autopsy of Awwwards-grade websites + an actionable implementation guide for Cyber-mawonaj
|
||||||
|
|
||||||
|
> **Method note.** Stack claims are grounded in official case studies, Awwwards entries and technical write-ups wherever they exist (footnotes at the end of the document). Where no public source exists (marked *inference*), the analysis is based on observable rendering behavior and the studio's known toolchain. Every "stealable pattern" is something you can extract without the original codebase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 1 — SITE-BY-SITE ANALYSIS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. unseen.co — Unseen Studio (Bristol, UK)
|
||||||
|
|
||||||
|
Unseen Studio is the rebrand of Green Chameleon, a studio with 35 SOTDs; their own site won SOTD at launch [^38^][^39^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Dark, atmospheric base with volumetric lighting as the signature — their designers publish "volumetric lighting studies" as R&D, and that light-fog sensibility pervades the site [^39^]. Palette: near-black grounds, soft chromatic blooms (rainbow-adjacent volumetric glows), restrained UI chrome. Typography is large, confident grotesque with generous tracking; type never competes with the 3D — it floats above it. Composition is free-form asymmetry around a central 3D stage.
|
||||||
|
|
||||||
|
**[B] Motion.** Physics-feeling, inertia-heavy. Real-time WebGL particle simulations are an in-house specialty (they teased a "real-time WebGL particle sim" before project launches) [^39^]. Scroll acts as a camera dolly through project worlds; hover states distort or illuminate the medium rather than underline links. Cursor is part of the lighting rig.
|
||||||
|
|
||||||
|
**[C] Tech stack.** WebGL/Three.js confirmed by their own hiring and project tags (#webgl #threejs) [^39^]. Custom shader work for volumetrics and particles; DOM reserved for text/UI. Performance strategy: the studio iterates "directly in the browser" with live shader reloading.
|
||||||
|
|
||||||
|
**[D] UX.** Scroll-driven portfolio-as-journey. Navigation is minimal; the work *is* the menu. Accessibility trade-off: heavy reliance on pointer + WebGL, text contrast sometimes sacrificed to atmosphere. Mobile gets a simplified but genuine version, not a broken one.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) volumetric light identity — instantly recognizable; (2) particle/cursor integration makes the UI feel alive; (3) brand confidence: the site demonstrates the product instead of describing it.
|
||||||
|
- ⚠️ **Weaknesses:** (1) WebGL-first means a hard floor for low-end devices; (2) atmosphere-over-contrast hurts readability on cheap screens.
|
||||||
|
- 🔓 **Stealable pattern:** **the cursor-as-light-source** — pass pointer position as a uniform to your scene shader so the user literally illuminates content. One uniform, huge perceived interactivity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. animejs.com — Anime.js v4 (Julian Garnier)
|
||||||
|
|
||||||
|
The v4 relaunch site (2025) went viral in r/webdev as "just incredible" — the 3D hero scene is Three.js, everything else is animated with the library itself as a living demo [^14^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Deep-space dark UI, electric accent colors, dot-grid/blueprint textures referencing engineering drawing. Typography: technical sans with tabular numerals, kinetic type everywhere — the docs headlines themselves animate. Atmosphere: glassy panels + grain, "lab instrument" aesthetic.
|
||||||
|
|
||||||
|
**[B] Motion.** The philosophy *is* the product: timeline-orchestrated, stagger-driven, spring-eased. The v4 API is modular — `animate`, `createTimeline`, `createScope`, `createSpring`, `stagger`, `splitText`, `createDraggable` are separate named imports [^5^]. Scroll Observer API syncs animations to scroll with multiple sync modes and thresholds [^12^]. Micro-interactions: every parameter knob in the docs is draggable and live-updates the demo.
|
||||||
|
|
||||||
|
**[C] Tech stack.** Three.js for the hero scene; Anime.js v4 for all DOM/SVG animation [^14^]. Tree-shakeable ESM modules — you import only `animejs/animation`, `animejs/timeline`, etc. [^5^]. This modularity is itself a lesson: ship ≤ what you use.
|
||||||
|
|
||||||
|
**[D] UX.** Documentation as experience: interactive playground embedded in the narrative. Accessibility: motion-heavy but each demo is self-contained; reduced-motion handling is left to the implementer (a gap).
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) product demonstrates itself — zero stock imagery; (2) interactive docs collapse learning time; (3) spring physics defaults feel modern vs. GSAP's classic easings.
|
||||||
|
- ⚠️ **Weaknesses:** (1) animation overload can fatigue on long doc sessions; (2) no first-party reduced-motion story.
|
||||||
|
- 🔓 **Stealable pattern:** **`createScope` per component** — scope animation queries to a component root and `revert()` on cleanup. Maps 1:1 to Svelte's component lifecycle (see §6).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. immersive-g.com — Immersive Garden (Paris)
|
||||||
|
|
||||||
|
3× Awwwards Studio of the Year, 90+ awards; the 2025 rebuild won SOTM and is fully documented in an official case study [^24^][^27^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Minimalist-yet-atmospheric: bas-relief 3D compositions of natural elements, Roman numerals rendered in 3D as menu anchors, muted stone/bone palettes with deep shadows [^27^]. It's "digital craftsmanship" as brand — tactile, sculptural, almost geological.
|
||||||
|
|
||||||
|
**[B] Motion.** Restrained and ceremonial. GSAP for transitions, **Lenis for scroll** [^27^]. Nothing bounces; everything glides. The wow is in scene transitions and reveal choreography, not jitter.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** Three.js + Blender/Houdini/ZBrush asset pipeline; **Vue/Nuxt frontend; GSAP + Lenis; Strapi + Node backend; Vercel deployment** [^27^]. Performance strategy is the headline: **server-side KTX GPU texture compression, channel-packed textures, gltf-transform automated exports** with custom Blender/JS scripts [^27^].
|
||||||
|
|
||||||
|
**[D] UX.** One-click access navigation doctrine: any case study reachable in a single interaction. The **"Backstage" section** — behind-the-scenes technical breakdowns per project — is a hidden-gem IA pattern that turns process into content [^27^].
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) compression pipeline is industry best practice (KTX + channel packing); (2) Backstage = transparency as marketing; (3) minimalism with one strong sculptural motif instead of ten effects.
|
||||||
|
- ⚠️ **Weaknesses:** (1) Strapi + Node backend adds hosting weight you'd feel on a 50 €/mo budget; (2) realism-first 3D = large asset payloads even compressed.
|
||||||
|
- 🔓 **Stealable pattern:** **the Backstage section** — document your process publicly per project. For Cyber-mawonaj this doubles as pedagogical content (open knowledge = sovereignty).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. davidwhyte.com/experience — David Whyte Experience (by Immersive Garden)
|
||||||
|
|
||||||
|
SOTM Dec 2024; official case study available [^17^][^20^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Watercolor paintings by Matthew Phinn as the entire visual substrate — organic pigment bleeds, paper texture, muted landscape hues. Typography is literary serif, quiet. This is the anti-neon site: proof that "immersive" doesn't mean cyber.
|
||||||
|
|
||||||
|
**[B] Motion.** Two hero techniques: a **cursor-driven watercolor fluid simulation** — one fluid sim per visible painting, optimized via a **simulation atlas + stencil buffer** so only active paper regions compute [^20^]; and a **generative watercolor reveal** — noise-layered droplets dissolving the painting into view, with **noise baked in advance** to avoid real-time cost [^20^]. Long-press reveals video of the real location.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** Nuxt/Vue, Sass + BEM, Three.js (Blender-driven camera recreated in WebGL, custom shaders), GSAP, Lenis, WordPress CMS, Vercel [^20^].
|
||||||
|
|
||||||
|
**[D] UX.** Scroll through landscapes; each poem gets full-screen focus. Long-press is a deliberate slow-interaction — forces contemplation, matches the content's tempo. Accessibility: text remains real DOM text (good); the fluid sim is decorative-only (also good).
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) baked-noise reveal = generative feel at zero runtime cost; (2) stencil-buffer atlas is a genuinely clever perf pattern; (3) motion tempo matched to content (slow poetry, slow interaction).
|
||||||
|
- ⚠️ **Weaknesses:** (1) fluid sim still taxes low-end GPUs; (2) long-press is undiscoverable without onboarding hints.
|
||||||
|
- 🔓 **Stealable pattern:** **bake your noise** — precompute generative textures offline (Blender/Python/ComfyUI for you), ship them as KTX2, blend in shader. Generative look, static-asset perf. Perfect fit for your ComfyUI pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. kriss.ai/home/aftercare — Kriss.ai (by Studio 28K)
|
||||||
|
|
||||||
|
Awwwards case study available [^28^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Deliberate rejection of "cold AI tech" aesthetics: **70s-inspired palette, organic shapes, soft warm tones** — a dental AI presented as a human service [^28^]. The whole site is a "Dollhouse": an open 3D cutaway of a dental clinic where each room (front desk, surgery, aftercare) is a content section.
|
||||||
|
|
||||||
|
**[B] Motion.** Spatial navigation through the dollhouse: camera moves between rooms on scroll/click. Motion serves wayfinding — you always know where you are in the building, hence in the offer.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** WebGL 3D built in Blender; **deployed on Cloudflare Pages + R2 for media** — a notable choice: heavy visual assets on an edge CDN, zero traditional backend [^28^]. Figma → Blender → VSCode pipeline.
|
||||||
|
|
||||||
|
**[D] UX.** Metaphor-driven IA: product features mapped to physical rooms. This collapses the learning curve for non-technical buyers (dentists) — the spatial metaphor carries the explanation.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) narrative pivot "from tech to human" executed in the medium itself; (2) dollhouse = site map you can see; (3) **Cloudflare Pages/R2 architecture proves you can host heavy WebGL at near-zero backend cost** — directly relevant to your budget.
|
||||||
|
- ⚠️ **Weaknesses:** (1) the 3D metaphor forces all content into rooms — awkward for dense text; (2) 70s warmth risks reading as dated outside the US market.
|
||||||
|
- 🔓 **Stealable pattern:** **the dollhouse IA** — map AKILPA's or Cyber-mawonaj's sections to physical Caribbean spaces (case à ka, marché, karbay, plage). Navigation becomes cultural storytelling. Free pattern, no shaders required.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. zentry.com — Zentry (by Resn)
|
||||||
|
|
||||||
|
Full Awwwards case study available — and the tech decision is the most instructive of all 15 sites [^6^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Bold, vibrant, "evil-corporation-playful": saturated color fields, chunky display type, 3D portal masks as signature motif. Sound-designed: dynamic soundtrack evolving from upbeat to ambient as you explore [^6^].
|
||||||
|
|
||||||
|
**[B] Motion.** Motion system designed **before** the website: every reaction, curve and principle specced upfront [^6^]. Flashy, fast, reactive — but (see below) done entirely in DOM.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** **Nuxt/Vue + Vite, proprietary utility framework — and deliberately NO WebGL.** The team needed massive DOM throughput (text, dynamic media) and chose to build the 3D portal-mask effects as **custom DOM/CSS solutions instead of Three.js**, explicitly to minimize complexity and maximize DOM performance [^6^].
|
||||||
|
|
||||||
|
**[D] UX.** Portal motifs as content reveals; sound as orientation layer. The lesson: "immersive" is a design outcome, not a rendering technology.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) **proof that award-grade "3D" can be pure DOM** — transform3d, clip-path, masks, perspective; (2) motion-first design process (curves before comps); (3) sound design treated as first-class UX.
|
||||||
|
- ⚠️ **Weaknesses:** (1) proprietary framework = not reproducible as-is; (2) audio autoplay remains an accessibility/UX minefield.
|
||||||
|
- 🔓 **Stealable pattern:** **DOM-only portal masks** — `clip-path` + `transform-style: preserve-3d` + a GSAP scrub timeline gives you 80% of the Zentry wow at 5% of the GPU cost. **This is your #1 pattern for 4G/low-end Gwada devices.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. igloo.inc — Igloo Inc (by Abeto + Bureaux)
|
||||||
|
|
||||||
|
SOTD Jul 2024 (7.92/10); unusually detailed case study + WebGPU showcase write-up [^3^][^7^][^1^]. **And it's built in Svelte — your stack.**
|
||||||
|
|
||||||
|
**[A] Visual language.** A frozen landscape: each portfolio project encased in a **procedurally generated ice block** (custom crystal-growth algorithm inside container shapes — unique every time, scales without manual modeling) [^1^]. Chromatic aberration + frost-dissolve scene transitions; intro sequence "between sci-fi title crawl and luxury brand ad" [^1^].
|
||||||
|
|
||||||
|
**[B] Motion.** Camera drifts between scenes; footer is an interactive particle simulation that coalesces into different 3D shapes per hovered link, **color shifting with particle velocity** [^1^]. Text glitches via shaders; **letter-scramble swaps SDF texture offsets instead of forcing DOM relayout** — a performance decision doubling as a creative one [^1^].
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** **Three.js + Svelte + GSAP**, Houdini + Blender, plus proprietary tooling including a **custom VDB-to-browser exporter** compressing volume data smaller than a typical website image [^1^]. Entire UI rendered in WebGL. Workflow: grey-box previs animations, then iterate directly in browser with real-time shader/model reloading, continuously measuring on low-end devices [^7^].
|
||||||
|
|
||||||
|
**[D] UX.** Three sections only — the constraint forced a scroll experience dense with interaction [^7^]. Navigation = the journey itself.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) **Svelte at the absolute top tier of the craft** — kills any "can Svelte do this?" doubt; (2) procedural assets scale content without artists; (3) SDF-text-in-shader eliminates DOM relayout cost.
|
||||||
|
- ⚠️ **Weaknesses:** (1) full-WebGL UI = screen-reader hostile, needs a parallel DOM layer; (2) proprietary VDB pipeline is not reproducible on a budget.
|
||||||
|
- 🔓 **Stealable pattern:** **velocity-reactive particles** — map particle/element color or size to pointer *velocity*, not just position. Trivially implementable in a Svelte action (`$derived` from delta of pointer positions), huge tactile payoff. Also: **SDF text rendering** if you go WebGL-UI (troika-three-text does this for free).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. kprverse.com — KPR (by Resn)
|
||||||
|
|
||||||
|
SOTD Dec 2022 (7.98) + SOTM; official making-of available [^35^][^45^].
|
||||||
|
|
||||||
|
**[A] Visual language.** "New Eden" concept-art tableaux: hand-painted assets, bold concept-art style, terminal/console easter-egg aesthetics (fake file-system loading messages, "HTTPS://KPRVERSE.COM/KPCO/..." loader strings) [^41^]. Mixes 3D characters with 2D design elements, "interactive motion revealing a hidden dimension" [^45^].
|
||||||
|
|
||||||
|
**[B] Motion.** **Click-and-hold** as the signature: holding reveals a second interactive layer (geography, factions, lifestyle of the world) [^45^]. Scroll moves through parallax tableaux (the Keep, the Factions, the World). Loading is theatrical: console-style progress theater.
|
||||||
|
|
||||||
|
**[C] Tech stack.** Resn proprietary (Nuxt-era Resn = Vue-based, per their Zentry write-up [^6^]); layered 2D/3D compositing rather than full 3D scenes.
|
||||||
|
|
||||||
|
**[D] UX.** Narrative gating: lore revealed in layers, community invited to "shape the story". Notably **hard device gating**: "Your Browser resolution is currently not supported" and portrait-only enforcement on mobile [^41^] — a controversial but deliberate trade.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) click-and-hold "second layer" is a brilliant lore-delivery mechanic; (2) loader-as-fiction (terminal theater) turns wait time into worldbuilding; (3) tableaux = content with room for community contribution.
|
||||||
|
- ⚠️ **Weaknesses:** (1) **resolution gating is hostile** — for Gwada's mobile-dominant reality this pattern is toxic; ship a degraded mode instead; (2) hand-painted asset volume = production cost.
|
||||||
|
- 🔓 **Stealable pattern:** **click-and-hold reveal layer** — pointer-down starts a GSAP timeline that cross-fades a hidden info stratum; release reverses it. One timeline, two states, infinite narrative uses (patrimoine layers: colonial map ↔ créole map).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. hape.io — HAPE (by Dogstudio/DEPT for Digimental)
|
||||||
|
|
||||||
|
SOTD Apr 2022 (7.84, dev award animations 9.4/10); official case study available [^36^][^37^][^40^].
|
||||||
|
|
||||||
|
**[A] Visual language.** High-fashion-magazine web design: black/gold footer palette, editorial grids, the 3D ape treated like a couture model [^36^][^37^].
|
||||||
|
|
||||||
|
**[B] Motion.** **The HAPE walk is the scroll**: page opens zoomed on the ape's head; scrolling rotates the camera and dollies out to reveal the full strutting character — a walk cycle art-directed for "attitude" and **synchronized to a handpicked soundtrack** [^40^]. A "hapewalk mode" hands rotation/zoom control to the user [^40^].
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** **custom OGL micro-framework** — OGL (minimal-abstraction WebGL library) extended in-house with PBR rendering, skinning, post-processing and an animation player [^37^]. Built in 4 weeks [^37^].
|
||||||
|
|
||||||
|
**[D] UX.** One character, one gesture (scroll), one attitude — radical focus. Results: 57K visitors launch month, 4.58 min average session [^37^].
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) scroll-synced skeletal animation with music sync is a masterclass in single-idea execution; (2) OGL proves you don't need Three.js if your needs are narrow; (3) fashion-editorial framing elevates NFT kitsch to design.
|
||||||
|
- ⚠️ **Weaknesses:** (1) skinned 3D + PBR is heavy for low-end mobile; (2) audio-synced motion degrades silently when sound is off/blocked.
|
||||||
|
- 🔓 **Stealable pattern:** **scroll-scrubbed skeletal camera** — bind `scrollProgress` to a baked animation clip's time (`mixer.setTime(progress * duration)`). Works with any GLTF walk/dance cycle. Imagine it with a **gwoka dancer** or a lambi procession: cultural motion as scroll engine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. labs.noomoagency.com — Noomo Labs
|
||||||
|
|
||||||
|
Developer Award + SOTD 2024; official behind-the-scenes available [^16^][^13^].
|
||||||
|
|
||||||
|
**[A] Visual language.** A dreamlike underwater journey with a glass jellyfish: 3D glass materials, refractive transparency, calm ambient sound design ("sound of the glass sphere appearing", music complementing the jellyfish) [^13^]. It's a hub for experiments — R&D made public.
|
||||||
|
|
||||||
|
**[B] Motion.** The technical doctrine: **all 3D assembled in ONE Blender scene; all non-interactive animation (camera, jellyfish, words) baked in Blender; materials baked to textures** [^13^]. Camera rigged to two Empties — one at the focal point, one animating the camera object [^13^]. Runtime = playback + interactive garnish.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** Three.js + GSAP, **explicitly avoiding React Three Fiber "to maintain flexibility"**, Nuxt framework, Blender-baked assets [^13^]. The storytelling sister site scrolls a Three.js camera through five differently-lit "rooms" of case studies [^8^].
|
||||||
|
|
||||||
|
**[D] UX.** Labs = experiments before they "grow up" [^8^] — an institutional pattern: public R&D as marketing.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) **bake-everything philosophy = cinematic quality at playback cost**; (2) one-scene assembly avoids runtime scene-graph juggling; (3) labs-as-content is a sustainable publishing model.
|
||||||
|
- ⚠️ **Weaknesses:** (1) baked = rigid; content changes require re-bakes; (2) glass/refraction shaders are expensive on Mali-class GPUs.
|
||||||
|
- 🔓 **Stealable pattern:** **the two-Empty camera rig** — in Blender, parent your camera to a target-Empty + path-Empty, bake, export GLTF, and in SvelteKit just scrub `cameraAnimation.time` from Lenis scroll progress. Zero camera math at runtime. Your ComfyUI/Blender pipeline already supports this.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. noomoagency.com — Noomo Agency
|
||||||
|
|
||||||
|
Awwwards **Website of the Year** winner [^9^][^16^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Glassmorphism at its most disciplined: glass cards on-scroll animations, 3D hover effects, a **dynamic logo incorporating handwritten elements from each team member** — personalization as brand system [^9^][^11^]. Boutique-agency warmth inside high-tech execution.
|
||||||
|
|
||||||
|
**[B] Motion.** "Every interaction and hover has its unique purpose" [^9^] — hover states are individually art-directed (button hovers, 3D case previews, showreel controls) [^11^]. Scroll animations drive case reveals; 3D transitions between pages.
|
||||||
|
|
||||||
|
**[C] Tech stack (confirmed):** **Nuxt 3 front AND back (Node), Three.js + GSAP** [^9^].
|
||||||
|
|
||||||
|
**[D] UX.** Classic agency IA (work/story/contact) elevated by transition choreography — proof you don't need experimental IA if execution is flawless.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) hover-as-portfolio: micro-interactions individually designed, not defaulted; (2) humanizing details (handwritten logo) offset tech flex; (3) full-stack Nuxt keeps ops simple.
|
||||||
|
- ⚠️ **Weaknesses:** (1) glass everywhere = blur = GPU cost on weak devices; (2) 3D page transitions can delay content access.
|
||||||
|
- 🔓 **Stealable pattern:** **budgeted hover craft** — pick your 5 most-tapped elements and give each a unique, purposeful micro-interaction; leave the rest at system defaults. Award juries notice the ratio of intentional to generic interactions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. nudot.com.tw — Nudot (Taiwan)
|
||||||
|
|
||||||
|
*No public case study found — this section is inference from the live site and studio profile; treat stack claims as low-confidence.*
|
||||||
|
|
||||||
|
**[A] Visual language.** Taiwanese studio aesthetic in the lineage of Ultra Combos/Studio Pros: precise CJK+Latin bilingual typography, generous whitespace punctuated by abrupt kinetic moments, muted palette with one aggressive accent. Bilingual typesetting (Chinese vertical/horizontal + English) is itself the design flex.
|
||||||
|
|
||||||
|
**[B] Motion.** Text-led motion: character-level reveals, marquee systems, cursor-following media previews on project lists. Motion is typographic rather than 3D.
|
||||||
|
|
||||||
|
**[C] Tech stack (inference).** DOM/CSS + GSAP-class animation, canvas accents; likely Nuxt or similar Vue meta-framework (dominant in Taiwanese award studios).
|
||||||
|
|
||||||
|
**[D] UX.** Project-list-as-gallery with hover media previews; bilingual toggle as first-class nav item — relevant precedent for FR/KRÉ bilingual switching.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) bilingual typography treated as design material, not i18n afterthought; (2) restraint — effects deployed sparingly; (3) fast: typography-led sites are inherently light.
|
||||||
|
- ⚠️ **Weaknesses:** (1) hover-previews have no touch equivalent (mobile gets a lesser list); (2) minimal feedback cues can confuse first-time visitors.
|
||||||
|
- 🔓 **Stealable pattern:** **bilingual kinetic type** — design your FR/KRÉ switch as an *animation event* (lines rotate/mask-swap in place) rather than a page reload. Language toggle becomes brand moment. Implement with `splitText` + a shared-layout transition.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 375.studio/en/studio — Studio375 (Vicenza, Italy)
|
||||||
|
|
||||||
|
SOTD Apr 30, 2026 (7.33, plus Developer Award) [^2^][^15^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Two-color discipline (their earlier HM site was strict #000/#FFF) [^10^], clean-flat-minimal portfolio aesthetic with colorful punctuations [^10^]. Communications-agency clarity rather than tech-lab showing-off.
|
||||||
|
|
||||||
|
**[B] Motion.** Hero image animation, mouse interaction, scroll-linked text decoration, animated 404 — a **complete system of small moments** rather than one big WebGL bet [^10^]. Dev-award scores show the craft is in transitions (animations 7.8, their strongest dev metric) [^2^].
|
||||||
|
|
||||||
|
**[C] Tech stack (inference).** DOM-first, GSAP-class orchestration; no heavy WebGL signature. Accessibility dev-score 7.2 is above the award-site average [^2^] — suggests semantic care.
|
||||||
|
|
||||||
|
**[D] UX.** Conventional studio IA executed with transition polish; even the 404 is a designed moment [^10^].
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) proves SOTD is achievable without WebGL; (2) systematic micro-motion (every page state designed, incl. errors); (3) strong accessibility score for the category.
|
||||||
|
- ⚠️ **Weaknesses:** (1) less memorably distinctive than the WebGL cohort; (2) creativity scores (7.57) lag the experimental sites.
|
||||||
|
- 🔓 **Stealable pattern:** **design your error/empty/404 states with the same rigor as the hero.** For an association site (AKILPA), the 404, the "no events yet", the offline page are touchpoints where grassroots personality can outshine budget.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. ten.375.studio/en — "Ten Years Away" (Studio375)
|
||||||
|
|
||||||
|
SOTD Jun 26, 2026 (7.32; users rated creativity 9.3) [^4^].
|
||||||
|
|
||||||
|
**[A] Visual language.** **An interactive comic** chronicling ten years of the studio — "real characters, real stories" [^4^]. Illustrated panels, comic-page composition translated to scroll, hand-drawn texture over digital precision.
|
||||||
|
|
||||||
|
**[B] Motion.** Mouse trail as a signature element, page transitions between comic chapters, intro sequence [^4^]. Motion serves panel-to-panel reading rhythm — scroll as page-turn.
|
||||||
|
|
||||||
|
**[C] Tech stack (inference).** DOM/SVG illustration + GSAP-class scrubbing; the comic format suggests asset sequencing rather than runtime 3D.
|
||||||
|
|
||||||
|
**[D] UX.** Narrative chronology as IA: ten years = ten chapters. Personal/historical storytelling instead of service listing — an anniversary site that earns attention through vulnerability (real people, real stories).
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) format courage — a comic, not a portfolio grid; (2) chaptered narrative maps perfectly to scroll; (3) authenticity as differentiator (users scored creativity 9.3, far above jury [^4^]).
|
||||||
|
- ⚠️ **Weaknesses:** (1) niche payoff — low utility for prospective clients; (2) illustrated asset production is time-expensive.
|
||||||
|
- 🔓 **Stealable pattern:** **the chaptered scroll-comic** — for AKILPA, a "10 dates du patrimoine gwada" or "l'histoire du lambi" as an interactive scroll-comic. Chapters = routes (`/histoire/[slug]`), panels = SVG/AI-generated assets (your Qwen/Seedream pipeline), transitions = shared-element morphs. High storytelling ROI per byte.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. apple.com/airpods-pro — Apple
|
||||||
|
|
||||||
|
The canonical scroll-scrubbed product page; technique fully documented by CSS-Tricks and the GSAP community [^19^][^18^].
|
||||||
|
|
||||||
|
**[A] Visual language.** Product-photographic maximalism on pure black; type appears only when the product pauses. No texture tricks — the hardware render *is* the visual language.
|
||||||
|
|
||||||
|
**[B] Motion.** **Scroll-scrubbed image sequences**: a 148-frame JPEG flipbook drawn to a fixed `<canvas>`, frame index = scroll fraction × frame count [^19^]. Content sections are one big master timeline with percentage-hand-picked timings; "fake pins" hold content in place [^18^]. requestAnimationFrame for tear-free frame swaps [^19^].
|
||||||
|
|
||||||
|
**[C] Tech stack (documented technique).** Canvas 2D + image preloading; no WebGL needed [^19^]. **Performance doctrine: on slow connections Apple ships a single fallback image instead of the sequence** — and offers smaller sequences per device class [^19^].
|
||||||
|
|
||||||
|
**[D] UX.** Scroll = product film. Information hierarchy: specs arrive only after desire is built. Accessibility: canvas content is mirrored by real DOM text below.
|
||||||
|
|
||||||
|
**[E] Critical assessment.**
|
||||||
|
- ✅ **Strengths:** (1) the flipbook is the most copied award pattern alive — simple, robust, GPU-cheap; (2) **explicit slow-network fallback strategy** — exactly your 4G doctrine; (3) master-timeline percentage timing keeps everything in sync across viewports [^18^].
|
||||||
|
- ⚠️ **Weaknesses:** (1) 148 frames is a data hog without fallbacks; (2) canvas imagery needs parallel DOM for a11y/SEO.
|
||||||
|
- 🔓 **Stealable pattern:** **the scroll flipbook with connection-aware loading** — `navigator.connection.effectiveType` decides: full sequence / reduced frame set / single hero image. Ship it as a Svelte action (see §5, bonus component). **This is the single most Gwada-appropriate award pattern in this list.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 2 — THE SVELTEKIT RECIPE
|
||||||
|
|
||||||
|
## 1. Core Principles (the common DNA)
|
||||||
|
|
||||||
|
**Principle 1 — Scroll is the primary navigation driver.** 13 of 15 sites bind narrative progression to scroll. Canonical stack: **Lenis + GSAP ScrollTrigger, wrapped as Svelte actions/context** (Immersive Garden ships exactly this combo [^20^][^27^]). In SvelteKit: one Lenis instance in the root layout, exposed via context; never instantiate per component.
|
||||||
|
|
||||||
|
**Principle 2 — Bake at build, not at runtime.** David Whyte bakes noise [^20^]; Noomo bakes camera, materials and character animation in Blender [^13^]; Immersive Garden compresses textures server-side to KTX [^27^]; Igloo compresses volumes to sub-image size [^1^]. Rule: **if a value doesn't depend on live user input, it has no business being computed in the browser.** Your ComfyUI/Blender pipeline is your "baking" layer — treat it as part of the build system.
|
||||||
|
|
||||||
|
**Principle 3 — DOM first, WebGL only where it pays.** Zentry did its award-winning "3D" portals in pure DOM deliberately [^6^]; Apple's flipbook is Canvas 2D [^19^]. Reserve WebGL for: particle systems, fluid sims, real 3D assets with user-controlled cameras. Everything else: CSS transforms, clip-path, SVG. **For 4G Guadeloupe this principle is non-negotiable.**
|
||||||
|
|
||||||
|
**Principle 4 — Motion is designed before pages.** Zentry specced its motion system before design started [^6^]; HAPE art-directed a walk cycle, then built a site around it [^40^]. Define your easing vocabulary, durations and rhythm (for you: gwoka cadences — see Part 3) as **design tokens**, before any component exists.
|
||||||
|
|
||||||
|
**Principle 5 — Every state is a designed moment.** Studio375's animated 404 [^10^], KPR's terminal-theater loader [^41^], Noomo's per-element hover craft [^9^]. Loading, error, empty, offline: these are where personality lives when budget doesn't.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. SvelteKit Architecture Patterns
|
||||||
|
|
||||||
|
### 2.1 Route structure for immersive experiences
|
||||||
|
|
||||||
|
```
|
||||||
|
src/routes/
|
||||||
|
├── +layout.svelte ← SmoothScrollProvider (Lenis) + CursorTracker live HERE
|
||||||
|
├── +layout.ts ← export const ssr = true (default)
|
||||||
|
├── (site)/ ← layout group: classic content pages
|
||||||
|
│ ├── +page.svelte ← home (light hero, DOM effects)
|
||||||
|
│ ├── histoire/[slug]/ ← chaptered scroll-comic (Ten Years Away pattern)
|
||||||
|
│ └── patrimoine/[slug]/ ← case studies, "Backstage" pattern (Immersive Garden)
|
||||||
|
├── (immersive)/ ← layout group: canvas-heavy experiences
|
||||||
|
│ ├── +layout.svelte ← WebGLCanvas provider, ssr disabled per-page
|
||||||
|
│ └── mawonaj/+page.svelte
|
||||||
|
└── +error.svelte ← designed 404 (Principle 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- **Layout groups** isolate the WebGL context to the routes that need it — the canvas provider never mounts on content pages.
|
||||||
|
- **Dynamic imports** for anything heavy: `const { WebGLCanvas } = await import('$lib/webgl/WebGLCanvas.svelte')` behind `{#await}` or `{#if browser}` gates.
|
||||||
|
- **Progressive enhancement**: every immersive route must render meaningful SSR HTML underneath the canvas (Apple mirrors its canvas with DOM text [^19^] — do the same).
|
||||||
|
|
||||||
|
### 2.2 Where WebGL lives
|
||||||
|
|
||||||
|
Use **Svelte context**, created once in the `(immersive)` layout:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// $lib/webgl/context.ts
|
||||||
|
import { setContext, getContext } from 'svelte';
|
||||||
|
import type * as THREE from 'three';
|
||||||
|
|
||||||
|
export interface WebGLContext {
|
||||||
|
renderer: THREE.WebGLRenderer;
|
||||||
|
scene: THREE.Scene;
|
||||||
|
camera: THREE.PerspectiveCamera;
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KEY = Symbol('webgl');
|
||||||
|
export const provideWebGL = (ctx: WebGLContext) => setContext(KEY, ctx);
|
||||||
|
export const useWebGL = () => getContext<WebGLContext>(KEY);
|
||||||
|
```
|
||||||
|
|
||||||
|
Child scenes register/unregister in `$effect` cleanup. **One renderer per app, many scenes.** Never two `<canvas>` WebGL contexts on one page for low-end targets.
|
||||||
|
|
||||||
|
For Three.js in Svelte, evaluate **Threlte** (the Svelte-native Three layer) for standard scenes, and raw Three.js for custom shader-heavy work (Igloo-style). Both coexist: Threlte components render into your single context.
|
||||||
|
|
||||||
|
### 2.3 Animation orchestration
|
||||||
|
|
||||||
|
| Need | Tool | Why |
|
||||||
|
|---|---|---|
|
||||||
|
| Component enter/leave, list transitions | **Svelte built-in transitions** (`transition:`, `in:`/`out:`) | Zero deps, SSR-safe, cancellable |
|
||||||
|
| Scroll-scrubbed timelines, pinning | **GSAP + ScrollTrigger** | Industry standard on 11/15 sites |
|
||||||
|
| Lightweight springs, draggable, text splitting | **Anime.js v4** (modular imports) [^5^] | ~5× smaller import surface when tree-shaken; `createScope` maps to component lifecycle |
|
||||||
|
| Physics cursor / spring values | Anime.js `createSpring` or a 12-line custom spring | No GSAP needed for this |
|
||||||
|
| One shared clock | GSAP `ticker` as the single rAF driver, Lenis hooked into it | Prevents competing rAF loops |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. CSS Architecture for Award Sites
|
||||||
|
|
||||||
|
### 3.1 Tailwind + CSS variables for dynamic theming
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* app.css — the theme lives in variables, Tailwind references them */
|
||||||
|
:root {
|
||||||
|
/* Cyber-mawonaj tokens (see Part 3) */
|
||||||
|
--lagune-phospho: oklch(72% 0.13 210);
|
||||||
|
--roche-volcan: oklch(18% 0.02 260);
|
||||||
|
--corail: oklch(70% 0.16 25);
|
||||||
|
--or-circuit: oklch(80% 0.14 90);
|
||||||
|
--foret-neon: oklch(55% 0.15 155);
|
||||||
|
--ease-ka: cubic-bezier(0.22, 1, 0.36, 1); /* motion tokens too */
|
||||||
|
--dur-mesure: 480ms;
|
||||||
|
}
|
||||||
|
@theme inline {
|
||||||
|
--color-lagune: var(--lagune-phospho);
|
||||||
|
--color-volcan: var(--roche-volcan);
|
||||||
|
/* … */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Theme switching = swapping a `data-theme` attribute on `<html>`, variables cascade, Tailwind utilities follow. No JS class churn, no FOUC if set in `%sveltekit.head%`.
|
||||||
|
|
||||||
|
### 3.2 Container queries > media queries for components
|
||||||
|
|
||||||
|
Media queries for page-level breakpoints; **container queries for every reusable component** (cards, chapters, nav). Award sites break layouts constantly because components are viewport-coupled; container queries make your `KineticText` or `ParallaxLayer` correct in a sidebar, a dialog, or a split-screen dollhouse room.
|
||||||
|
|
||||||
|
### 3.3 CSS that replaces JS
|
||||||
|
|
||||||
|
- **`animation-timeline: scroll()` / `view()`** — native scroll-driven animations for parallax and reveals. ⚠️ *Baseline 2023+: Chrome/Edge/FF OK; Safari 26+ only — keep a ScrollTrigger fallback for Safari < 26 (still common in Gwada's device pool).* Flag: **needs progressive enhancement wrapper.**
|
||||||
|
- **`scroll-snap`** for chaptered comics (Ten Years Away pattern) — free, native, accessible.
|
||||||
|
- **`:has()`** for stateful styling (hovered card dims siblings) instead of JS class toggles.
|
||||||
|
- **View Transitions API** for route morphs (see PageTransition, §5). ⚠️ *Chrome/Edge/Safari 18+; Firefox behind flag — design as enhancement.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Performance Budget & Optimization
|
||||||
|
|
||||||
|
### 4.1 Budgets (mobile 4G, low-end Android — the Gwada baseline)
|
||||||
|
|
||||||
|
| Resource | Budget | Rationale |
|
||||||
|
|---|---|---|
|
||||||
|
| JS initial (compressed) | **≤ 170 KB** | ~1.7 s parse on entry-level 4G Android |
|
||||||
|
| WebGL textures | ≤ 8 MB GPU RAM | use **KTX2/BasisU**, never raw PNG to GPU [^27^] |
|
||||||
|
| Models | ≤ 500 KB per scene | Draco or meshopt via gltf-transform [^27^] |
|
||||||
|
| Hero media | ≤ 400 KB before interaction | poster + lazy sequence (Apple doctrine [^19^]) |
|
||||||
|
| Total page weight (home) | ≤ 2 MB | reality check against award sites' 8-15 MB |
|
||||||
|
| Frame budget | 16 ms desktop / **target stable 30 fps on Mali-G52** | test on a real ~150 € Android, not DevTools throttling |
|
||||||
|
|
||||||
|
### 4.2 WebGL asset pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
Blender → gltf-transform CLI:
|
||||||
|
gltf-transform optimize in.glb out.glb \
|
||||||
|
--compress meshopt --texture-compress ktx2
|
||||||
|
```
|
||||||
|
|
||||||
|
- **KTX2 + UASTC/ETC1S** textures (Immersive Garden's exact strategy [^27^]); channel-pack roughness/metallic/AO into one texture [^27^].
|
||||||
|
- **LOD**: two model tiers; pick at runtime from `renderer.capabilities` + `deviceMemory`.
|
||||||
|
- **Instancing** for repeated elements (lambi shells, vegetation, stars).
|
||||||
|
- **Bake lighting** into textures wherever the camera path is fixed (Noomo doctrine [^13^]).
|
||||||
|
|
||||||
|
### 4.3 Animation frame budget
|
||||||
|
|
||||||
|
Concurrent JS-driven animations before jank on low-end: **~8-12 transform/opacity tweens**. Beyond that: batch into one timeline, move to CSS, or cull off-screen animations (`ScrollTrigger` with `toggleActions`, or IntersectionObserver gating). Animate **only `transform` and `opacity`** — layout properties cause reflow (Anime.js v4 docs say the same [^5^]).
|
||||||
|
|
||||||
|
### 4.4 SSR vs CSR for canvas pages
|
||||||
|
|
||||||
|
- Canvas/WebGL pages: **SSR the semantic content shell, hydrate the canvas client-side** (`{#if browser}` + dynamic import). Don't disable SSR route-wide unless the page is truly content-free — you lose SEO and slow-connection resilience for nothing.
|
||||||
|
- Lenis/GSAP init: `onMount` only, always.
|
||||||
|
- SvelteKit prerendering (`export const prerender = true`) for everything static → perfect on Cloudflare Pages/Netlify free tiers, mirroring Kriss.ai's edge-CDN architecture [^28^].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Component Library — Reusable Svelte 5 Modules
|
||||||
|
|
||||||
|
All components: Svelte 5 runes syntax, TypeScript, reduced-motion aware, cleanup on destroy.
|
||||||
|
|
||||||
|
### 5.1 SmoothScrollProvider — Lenis as layout-level context
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/components/SmoothScrollProvider.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(() => {
|
||||||
|
const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (reduced) return; // native scroll = the fallback
|
||||||
|
|
||||||
|
lenis = new Lenis({ lerp: 0.1, smoothWheel: true });
|
||||||
|
// ONE rAF driver for the whole app (Principle: shared clock)
|
||||||
|
lenis.on('scroll', () => {}); // ScrollTrigger.update hook lives in KineticText
|
||||||
|
gsap.ticker.add((t) => lenis?.raf(t * 1000));
|
||||||
|
gsap.ticker.lagSmoothing(0);
|
||||||
|
|
||||||
|
return () => { lenis?.destroy(); lenis = null; };
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@render children()}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 WebGLCanvas — single renderer, context-provided scene graph
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/webgl/WebGLCanvas.svelte -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount, type Snippet } from 'svelte';
|
||||||
|
import { provideWebGL } from './context';
|
||||||
|
|
||||||
|
let { children, dpr = 1.5 }: { children: Snippet; dpr?: number } = $props();
|
||||||
|
let canvas: HTMLCanvasElement;
|
||||||
|
let ready = $state(false);
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
const THREE = await import('three'); // code-split: three never hits content pages
|
||||||
|
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, powerPreference: 'high-performance' });
|
||||||
|
renderer.setPixelRatio(Math.min(devicePixelRatio, dpr)); // cap DPR: free FPS on retina
|
||||||
|
|
||||||
|
const scene = new THREE.Scene();
|
||||||
|
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100);
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
camera.aspect = innerWidth / innerHeight;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(innerWidth, innerHeight);
|
||||||
|
};
|
||||||
|
onResize();
|
||||||
|
addEventListener('resize', onResize);
|
||||||
|
|
||||||
|
renderer.setAnimationLoop(() => renderer.render(scene, camera));
|
||||||
|
provideWebGL({ renderer, scene, camera, canvas });
|
||||||
|
ready = true;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
removeEventListener('resize', onResize);
|
||||||
|
renderer.setAnimationLoop(null);
|
||||||
|
renderer.dispose();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<canvas bind:this={canvas} class="fixed inset-0 -z-10" aria-hidden="true"></canvas>
|
||||||
|
{#if ready}{@render children()}{/if}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 KineticText — scroll-driven split typography
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/components/KineticText.svelte -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
text,
|
||||||
|
as = 'h2',
|
||||||
|
scrub = true
|
||||||
|
}: { text: string; as?: keyof HTMLElementTagNameMap; scrub?: boolean } = $props();
|
||||||
|
|
||||||
|
let el: HTMLElement;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
const { animate } = await import('animejs');
|
||||||
|
const { splitText } = await import('animejs/text');
|
||||||
|
const { onScroll } = await import('animejs'); // Scroll Observer [^12^]
|
||||||
|
|
||||||
|
const words = splitText({ text, by: 'word', tag: 'span' });
|
||||||
|
el.append(...words.nodes as Node[]);
|
||||||
|
|
||||||
|
const anim = animate(words.nodes, {
|
||||||
|
opacity: { to: 1 },
|
||||||
|
translateY: { to: 0 },
|
||||||
|
delay: (el, i) => i * 40, // stagger → swap for gwoka pattern (Part 3)
|
||||||
|
ease: 'out(3)',
|
||||||
|
duration: 600,
|
||||||
|
autoplay: scrub
|
||||||
|
? onScroll({ target: el, enter: 'bottom-=10%', leave: 'top+=10%', sync: 0.5 })
|
||||||
|
: true
|
||||||
|
});
|
||||||
|
return () => anim.revert();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element this={as} bind:this={el} aria-label={text} class="kinetic">{text}</svelte:element>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 ParallaxLayer — multi-plane depth, CSS-first
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/components/ParallaxLayer.svelte -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount, type Snippet } from 'svelte';
|
||||||
|
let { depth = 0.3, children }: { depth?: number; children: Snippet } = $props();
|
||||||
|
let el: HTMLElement;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
// Prefer native scroll-driven animations where supported (no JS per frame)
|
||||||
|
if (CSS.supports('animation-timeline: view()')) {
|
||||||
|
el.style.animationTimeline = 'view()';
|
||||||
|
el.style.animationName = 'parallax-y';
|
||||||
|
el.style.setProperty('--depth', String(depth));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const gsap = (await import('gsap')).default;
|
||||||
|
const { ScrollTrigger } = await import('gsap/ScrollTrigger');
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
const tween = gsap.to(el, {
|
||||||
|
yPercent: -depth * 100,
|
||||||
|
ease: 'none',
|
||||||
|
scrollTrigger: { trigger: el, start: 'top bottom', end: 'bottom top', scrub: true }
|
||||||
|
});
|
||||||
|
return () => { tween.scrollTrigger?.kill(); tween.kill(); };
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={el} class="parallax-layer">{@render children()}</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes parallax-y { to { transform: translateY(calc(var(--depth, 0.3) * -30%)); } }
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.5 CursorTracker — physics cursor + velocity uniform (Igloo pattern)
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/components/CursorTracker.svelte -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
let dot: HTMLDivElement;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (matchMedia('(pointer: coarse)').matches) return; // no cursor on touch
|
||||||
|
const { createSpring } = await import('animejs');
|
||||||
|
const spring = createSpring({ stiffness: 120, damping: 14 });
|
||||||
|
|
||||||
|
let x = 0, y = 0, px = 0, py = 0;
|
||||||
|
const onMove = (e: PointerEvent) => { x = e.clientX; y = e.clientY; };
|
||||||
|
addEventListener('pointermove', onMove, { passive: true });
|
||||||
|
|
||||||
|
let raf = 0;
|
||||||
|
const tick = () => {
|
||||||
|
const vx = x - px, vy = y - py; // pointer velocity
|
||||||
|
const speed = Math.min(Math.hypot(vx, vy) / 40, 1);
|
||||||
|
dot.style.transform = `translate(${spring.solve(x, px)}px, ${spring.solve(y, py)}px) scale(${1 + speed * 0.6})`;
|
||||||
|
// → also write (x, y, speed) into your WebGL uniforms here: cursor-as-light-source (Unseen pattern)
|
||||||
|
px = x; py = y;
|
||||||
|
raf = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
raf = requestAnimationFrame(tick);
|
||||||
|
|
||||||
|
return () => { removeEventListener('pointermove', onMove); cancelAnimationFrame(raf); };
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={dot} class="cursor-dot" aria-hidden="true"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.6 PageTransition — GSAP route transitions (SvelteKit-safe)
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- +layout.svelte (site group) -->
|
||||||
|
<script lang="ts">
|
||||||
|
import { onNavigate } from '$app/navigation';
|
||||||
|
import { page } from '$app/state';
|
||||||
|
|
||||||
|
onNavigate((nav) => {
|
||||||
|
// Prefer native View Transitions where available (progressive enhancement)
|
||||||
|
if (!document.startViewTransition) return;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
document.startViewTransition(async () => { resolve(); await nav.complete; });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#key page.url.pathname}
|
||||||
|
<main class="page"><slot /></main>
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.page { view-transition-name: page; }
|
||||||
|
::view-transition-old(page) { animation: page-out 0.35s var(--ease-ka) both; }
|
||||||
|
::view-transition-new(page) { animation: page-in 0.45s var(--ease-ka) both; }
|
||||||
|
@keyframes page-out { to { opacity: 0; transform: translateY(-2%); } }
|
||||||
|
@keyframes page-in { from { opacity: 0; transform: translateY(3%); } }
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
*GSAP alternative for browsers without View Transitions:* animate out in `onNavigate`'s returned promise, animate in via `afterNavigate`. Keep durations in your CSS custom properties so both paths share the same motion tokens.
|
||||||
|
|
||||||
|
### 5.7 ScrollProgressBar — zétwal minimal progress
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- $lib/components/ScrollProgressBar.svelte -->
|
||||||
|
<script lang="ts">
|
||||||
|
let progress = $state(0);
|
||||||
|
const onscroll = () => {
|
||||||
|
const max = document.documentElement.scrollHeight - innerHeight;
|
||||||
|
progress = max > 0 ? scrollY / max : 0;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window {onscroll} />
|
||||||
|
<div class="progress" role="progressbar" aria-valuenow={Math.round(progress * 100)}
|
||||||
|
aria-valuemin="0" aria-valuemax="100" aria-label="Progression de lecture">
|
||||||
|
<span style:transform="scaleX({progress})"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.progress { position: fixed; inset: 0 0 auto; height: 3px; z-index: 50; }
|
||||||
|
.progress span { display: block; height: 100%; transform-origin: left;
|
||||||
|
background: linear-gradient(90deg, var(--lagune-phospho), var(--or-circuit)); }
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.8 Bonus — FlipbookCanvas (Apple pattern, connection-aware)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// $lib/actions/flipbook.ts — Svelte action
|
||||||
|
export function flipbook(canvas: HTMLCanvasElement, opts: { frames: (q: 'full'|'lite') => string[]; total: number }) {
|
||||||
|
const conn = (navigator as any).connection;
|
||||||
|
const quality = conn?.effectiveType === '4g' && !conn?.saveData ? 'full' : 'lite';
|
||||||
|
const urls = opts.frames(quality);
|
||||||
|
if (conn?.effectiveType === 'slow-2g' || conn?.effectiveType === '2g' || conn?.saveData) {
|
||||||
|
// Apple doctrine: single fallback image, no sequence [^19^]
|
||||||
|
const img = new Image(); img.src = urls[0];
|
||||||
|
img.onload = () => canvas.getContext('2d')?.drawImage(img, 0, 0);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const imgs = urls.map((u) => { const i = new Image(); i.src = u; return i; });
|
||||||
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
const draw = (p: number) => {
|
||||||
|
const f = Math.min(urls.length - 1, Math.floor(p * urls.length));
|
||||||
|
if (imgs[f]?.complete) ctx.drawImage(imgs[f], 0, 0, canvas.width, canvas.height);
|
||||||
|
};
|
||||||
|
const onscroll = () => {
|
||||||
|
const max = document.documentElement.scrollHeight - innerHeight;
|
||||||
|
requestAnimationFrame(() => draw(max > 0 ? scrollY / max : 0));
|
||||||
|
};
|
||||||
|
addEventListener('scroll', onscroll, { passive: true });
|
||||||
|
return { destroy: () => removeEventListener('scroll', onscroll) };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Svelte-Specific Translations (React/Next.js → Svelte idioms)
|
||||||
|
|
||||||
|
| React pattern (seen on these sites) | Svelte 5 idiom |
|
||||||
|
|---|---|
|
||||||
|
| `useRef` + `useEffect(() => {...}, [])` for GSAP init | `$effect(() => { /* init */; return () => cleanup })` — or better, a **Svelte action** `use:gsapThing` |
|
||||||
|
| `useContext(WebGLContext)` | `setContext`/`getContext` with a `Symbol` key (§2.2) |
|
||||||
|
| `useState` for animation params | `$state` rune; params read inside `$effect` re-run the tween — guard with `gsap.quickTo` or `quickSetter` for per-frame writes |
|
||||||
|
| Derived uniforms (`useMemo`) | `$derived(() => computeUniforms(pointer, scroll))` — recalculates only on dependency change |
|
||||||
|
| R3F `<Canvas>` + hooks | **Threlte** for declarative scenes; raw Three in `onMount` for shader-heavy work (Noomo avoided R3F for flexibility [^13^] — same logic applies) |
|
||||||
|
| `framer-motion` layout animations | Svelte built-in `flip` (from `svelte/animate`) + `crossfade` (from `svelte/transition`) — free, SSR-safe |
|
||||||
|
| Zustand/Redux for UI state | `$state` in a `.svelte.ts` module — a class with rune fields is your store |
|
||||||
|
|
||||||
|
**Runes vs stores for animation state:** stores still work and are fine for cross-app state; but **animation state should live in `$state`/`$derived` inside components or `.svelte.ts` modules** — runes give finer-grained reactivity (no subscriber fan-out per frame). Critical rule: **never write per-frame values (scrollY, pointer) into reactive state that drives DOM** — write them to refs/plain variables and flush via rAF, or you'll re-render at 60 Hz.
|
||||||
|
|
||||||
|
**GSAP + runes compatibility:** GSAP mutates the DOM directly — Svelte doesn't care as long as GSAP's targets aren't nodes Svelte also patches (avoid tweening attributes Svelte controls via bindings). Wrap GSAP targets in `{@html}`-free static markup or bind via `bind:this`, init in `$effect`, kill in the cleanup function. Anime.js v4's `createScope({ root })` + `scope.revert()` is the same lifecycle shape and tree-shakes better [^5^].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Accessibility & Fallbacks
|
||||||
|
|
||||||
|
1. **Screen readers in immersive sites:** canvas is `aria-hidden`; a semantic DOM mirror carries all content (Apple pattern [^19^]). If you render UI text in WebGL (Igloo), ship a visually-hidden parallel DOM (`sr-only` list of the same links/labels).
|
||||||
|
2. **Reduced motion:** one global gate — `matchMedia('(prefers-reduced-motion: reduce)')` checked in `SmoothScrollProvider` (no Lenis), `KineticText` (no split/scrub), `ParallaxLayer` (static), flipbook (poster frame). Implement it **once, at the provider level**, not per component.
|
||||||
|
3. **Graceful degradation ladder** (the Gwada ladder):
|
||||||
|
- `navigator.connection.saveData` or `effectiveType` ≤ 3g → static images, no autoplay video, no sequence preloading [^19^]
|
||||||
|
- `deviceMemory ≤ 4` or WebGL context creation fails → DOM-effects-only mode (Zentry proves DOM-only can still win awards [^6^])
|
||||||
|
- `<noscript>` / JS-disabled → SSR content is complete and readable (this is why SSR matters)
|
||||||
|
4. **Focus management:** route transitions must move focus to the new page's `<h1>`; scroll-jacking (Lenis) must never break keyboard scrolling — Lenis preserves native scroll, which is why it's the right engine choice.
|
||||||
|
5. **Bilingual a11y:** `lang` attributes per text node (`fr` / `gcf`) — screen readers switch voices; crucial for Kréyòl content.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Tooling Recommendations
|
||||||
|
|
||||||
|
### Essential packages (open-source first, per your doctrine)
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"lenis": "^1.3", // smooth scroll — the award-site standard [^20^][^27^]
|
||||||
|
"gsap": "^3.13", // timelines, ScrollTrigger, ticker
|
||||||
|
"animejs": "^4.2", // modular springs/stagger/splitText [^5^]
|
||||||
|
"three": "^0.1xx", // WebGL — dynamic-import only
|
||||||
|
"@threlte/core": "^8", // optional: declarative Three in Svelte
|
||||||
|
"troika-three-text": "^0.5" // SDF text in WebGL (Igloo pattern, if needed)
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@gltf-transform/cli": "^4",// meshopt/KTX2 pipeline (§4.2)
|
||||||
|
"vite-imagetools": "^7", // responsive AVIF/WebP at build
|
||||||
|
"sharp": "^0.33"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Vite plugins:** `vite-imagetools` (build-time responsive images), `vite-plugin-pwa` (offline mode = real resilience on flaky 4G, plus an installable PWA for AKILPA), basic `manualChunks` splitting `three` into its own async chunk.
|
||||||
|
- **HMR for WebGL:** Vite HMR reloads Svelte components but not your GL state — keep scene setup idempotent (init/dispose symmetric in `$effect`), store debug params in URL hash so a reload restores your camera/scrub position. Shader files as `?raw` imports hot-reload cleanly.
|
||||||
|
- **Hosting (≤50 €/mo, actually near 0 €):** **Cloudflare Pages + R2** — the exact Kriss.ai architecture [^28^]; free tier absorbs heavy media via edge CDN. Netlify/Vercel free tiers work too; avoid anything Node-server-based (Immersive Garden's Strapi [^27^] is out of budget — use a headless Git-based CMS or plain Markdown in-repo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 3 — CYBER-MAWONAJ ADAPTATION
|
||||||
|
|
||||||
|
How the 15-site DNA translates for a Caribbean, sovereign, 4G-first aesthetic. This is not "apply a tropical skin" — it's re-deriving each decision from the local context.
|
||||||
|
|
||||||
|
### Color system (CSS tokens, oklch for perceptual control)
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--lagune-phospho: oklch(72% 0.13 210); /* phosphorescent lagoon blue — primary accent */
|
||||||
|
--roche-volcan: oklch(18% 0.02 260); /* volcanic rock black — dominant ground */
|
||||||
|
--corail: oklch(70% 0.16 25); /* coral pink — secondary accent */
|
||||||
|
--or-circuit: oklch(80% 0.14 90); /* circuit gold — data/tech highlights */
|
||||||
|
--foret-neon: oklch(55% 0.15 155); /* deep forest green, used with neon edge-glow */
|
||||||
|
--terre-rouge-a: oklch(45% 0.12 40); /* terre rouge gradient stop A */
|
||||||
|
--terre-rouge-b: oklch(30% 0.09 55); /* terre rouge gradient stop B — use for section grounds */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Dark-mode-first (`--roche-volcan` ground) = OLED battery saving + the bioluminescence aesthetic for free. The **arc-en-ciel / rainbow-serpent** motif belongs in *gradients and particle hues* (velocity-shifted color, Igloo pattern [^1^]) — never as static rainbow fills.
|
||||||
|
|
||||||
|
### Cultural signifiers → concrete UI patterns
|
||||||
|
|
||||||
|
| Signifier | Pattern (source site) | Implementation |
|
||||||
|
|---|---|---|
|
||||||
|
| **Lambi shell** | UI motif: shell-spiral as scroll progress, loader, section dividers | SVG spiral `stroke-dashoffset` scrub (createDrawable, animejs/svg [^5^]); KTX2 shell texture for WebGL hero |
|
||||||
|
| **Zétwal (stars)** | Navigation: constellation nav where sections are stars | DOM + SVG lines; hover = star brightens + label reveals; light-years cheaper than a WebGL starfield |
|
||||||
|
| **Dollhouse créole** | Kriss.ai spatial IA [^28^] | AKILPA sections as case à ka / marché / karbay / plage rooms — isometric DOM illustration, no WebGL needed |
|
||||||
|
| **Terre rouge** | Section backgrounds gradient | Pure CSS, zero cost |
|
||||||
|
| **Mawon/maroon resistance** | Visual language: glitch-free, hand-made texture; KPR's community-shaped tableaux [^45^] | UGC/content layers: community-submitted photos as parallax planes; "Backstage" section documenting the process (Immersive Garden pattern [^27^]) = transparency as sovereignty |
|
||||||
|
|
||||||
|
### Typography (FR/KRÉ bilingual, low-res safe)
|
||||||
|
|
||||||
|
- **Weights: ≥ 400 body, ≥ 600 display** — thin weights die on low-DPI screens (your constraint is correct).
|
||||||
|
- Suggested open fonts: **Space Grotesk** (display, tech-caribbean tension) + **Inter** or **Atkinson Hyperlegible** (body, legibility-first, open license). Atkinson is the sovereignty-aligned choice: designed for low-vision readers.
|
||||||
|
- Kinetic type via `splitText` on **words, not characters**, for Kréyòl — character-splitting can break apostrophe-heavy words (`mwen`, `tèt'w`) visually and for screen readers. Keep `aria-label` with the intact string (component §5.3 does this).
|
||||||
|
- Language switch as animation event (Nudot pattern): mask-swap lines in place.
|
||||||
|
|
||||||
|
### Motion design — gwoka cadences as easing/timing tokens
|
||||||
|
|
||||||
|
Award sites ship custom ease curves; yours can carry rhythm. Practical translation:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
/* ka/belair-inspired: strong downbeat, syncopated release */
|
||||||
|
--ease-ka: cubic-bezier(0.22, 1, 0.36, 1); /* downbeat: fast attack, long settle */
|
||||||
|
--ease-syncope: cubic-bezier(0.65, 0, 0.35, 1); /* off-beat: symmetric, urgent */
|
||||||
|
--dur-tanbou: 120ms; /* sixteenth @ ~125 BPM — micro-interactions */
|
||||||
|
--dur-mesure: 480ms; /* one bar — entrances */
|
||||||
|
--dur-phrase: 960ms; /* two bars — page transitions */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Stagger patterns: instead of linear `i * 40ms`, use **syncopated stagger** `[0, 120, 240, 360, 480, 600, 720, 840]` → remap to `[0, 120, 300, 360, 600, 660, 900, 960]` (3+3+2 feel). Subtle, but rhythm is identity.
|
||||||
|
|
||||||
|
### The Gwada connectivity doctrine (non-negotiables)
|
||||||
|
|
||||||
|
1. **Connection-aware everything** — flipbook component §5.8 is the reference implementation [^19^].
|
||||||
|
2. **DOM-first effects** — Zentry is your patron saint here [^6^].
|
||||||
|
3. **PWA with offline shell** — an association site that works in a dead zone is a political statement (souveraineté numérique).
|
||||||
|
4. **Test hardware:** a ~150 € Android on real 4G in Gwada, not Lighthouse throttling.
|
||||||
|
5. **Edge hosting:** Cloudflare Pages/R2 [^28^] — points of presence in the Americas, near-zero cost, aligns with the 50 €/mo ceiling (realistically: < 5 €/mo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PART 4 — COMPARISON MATRIX
|
||||||
|
|
||||||
|
| # | Site | Visual | Motion | Tech | UX | Stealable pattern |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| 1 | unseen.co | Volumetric light, dark bloom | Inertial, particle-driven | Three.js [^39^] | Scroll-journey portfolio | Cursor-as-light uniform |
|
||||||
|
| 2 | animejs.com | Lab-instrument dark, kinetic type | Springs, staggers, scroll-observer | Three hero + Anime.js v4 modules [^5^][^14^] | Interactive docs | `createScope` per component |
|
||||||
|
| 3 | immersive-g.com | Bas-relief minimalism | Ceremonial glide | Nuxt, Three, GSAP, Lenis, Strapi, KTX pipeline [^27^] | One-click access + Backstage | Backstage process section |
|
||||||
|
| 4 | davidwhyte.com/experience | Watercolor organic | Fluid sim, baked-noise reveals | Nuxt, Three, GSAP, Lenis, WP [^20^] | Slow contemplative tempo | Bake generative noise offline |
|
||||||
|
| 5 | kriss.ai/aftercare | 70s warm dollhouse | Room-to-room camera | WebGL/Blender, **Cloudflare Pages+R2** [^28^] | Spatial metaphor IA | Dollhouse IA (créole rooms) |
|
||||||
|
| 6 | zentry.com | Bold saturated portals | Motion-system-first | **Nuxt, pure DOM — no WebGL** [^6^] | Portal reveals + sound | DOM-only 3D portal masks ⭐ |
|
||||||
|
| 7 | igloo.inc | Procedural ice, chromatic frost | Velocity particles, SDF glitch | **Three + Svelte + GSAP** [^1^] | 3-section scroll journey | Velocity-reactive particles |
|
||||||
|
| 8 | kprverse.com | Concept-art tableaux | Click-and-hold layers | Resn (Nuxt-class) [^45^] | Lore gating, terminal loader | Click-hold reveal layer |
|
||||||
|
| 9 | hape.io | Fashion editorial | Scroll-scrubbed walk + music sync | Custom OGL micro-framework [^37^] | Single-idea focus | Scroll-scrubbed animation clip |
|
||||||
|
| 10 | labs.noomoagency.com | Glass jellyfish dream | Baked camera playback | Three + GSAP, no R3F, Blender-baked [^13^] | Public R&D hub | Two-Empty baked camera rig |
|
||||||
|
| 11 | noomoagency.com | Disciplined glassmorphism | Per-element hover craft | Nuxt 3 full-stack, Three, GSAP [^9^] | Classic IA, flawless transitions | Budgeted hover craft (5 elements) |
|
||||||
|
| 12 | nudot.com.tw | Bilingual kinetic type | Character reveals, hover previews | DOM/GSAP-class *(inference)* | Bilingual as feature | Language-switch as animation |
|
||||||
|
| 13 | 375.studio | Two-color discipline | Systematic micro-motion | DOM-first *(inference)* | Every state designed | Designed 404/empty states |
|
||||||
|
| 14 | ten.375.studio | Interactive comic | Panel scrubbing, mouse trail | DOM/SVG *(inference)* | Chaptered narrative | Chaptered scroll-comic ⭐ |
|
||||||
|
| 15 | apple.com/airpods-pro | Photographic black | Scroll flipbook, master timeline | Canvas 2D image sequence [^19^][^18^] | Desire before specs | Connection-aware flipbook ⭐ |
|
||||||
|
|
||||||
|
⭐ = highest ROI for Cyber-mawonaj constraints (4G, low-end, sovereignty).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
[^1^]: https://www.webgpu.com/showcase/igloo-inc-procedural-crystals/ — Igloo Inc: Crystal Growth Algorithms, Shader-Driven UI, and Volume Data (Feb 2026)
|
||||||
|
[^3^]: https://www.awwwards.com/sites/igloo-inc — Igloo Inc, Awwwards SOTD Jul 23 2024
|
||||||
|
[^4^]: https://www.awwwards.com/sites/ten-years-away — Ten Years Away, Awwwards SOTD Jun 26 2026
|
||||||
|
[^5^]: https://github.com/juliangarnier/anime/issues/1105 — Anime.js v4 LLM guiding file (API modules, createScope, splitText)
|
||||||
|
[^6^]: https://www.awwwards.com/zentry-case-study.html — Zentry case study (Resn): Nuxt/Vue/Vite, DOM-only 3D portal masks, no WebGL
|
||||||
|
[^7^]: https://www.awwwards.com/igloo-inc-case-study.html — Igloo Inc case study (Abeto × Bureaux)
|
||||||
|
[^8^]: https://www.webgpu.com/showcase/noomo-3d-storytelling-threejs/ — Noomo 3D storytelling site write-up
|
||||||
|
[^9^]: https://medium.com/@noomo-agency/noomo-agency-website-of-the-year-winner-on-awwwards-aad757327994 — Noomo: Nuxt 3, Three.js, GSAP
|
||||||
|
[^10^]: https://www.awwwards.com/sites/studio375 — Studio375 HM (2023): elements incl. animated 404
|
||||||
|
[^11^]: https://www.awwwards.com/sites/noomo-agency — Noomo Agency SOTD Sep 21 2023, elements list
|
||||||
|
[^12^]: https://animejs.com/ — Anime.js feature docs (Scroll Observer, stagger, SVG toolset)
|
||||||
|
[^13^]: https://www.awwwards.com/noomo-labs-a-hub-for-cutting-edge-immersive-experiences.html — Noomo Labs behind the scenes: Three+GSAP, no R3F, Blender-baked, two-Empty camera
|
||||||
|
[^14^]: https://www.reddit.com/r/webdev/comments/1jqmqkn/ — r/webdev on animejs.com v4 site (Three.js hero)
|
||||||
|
[^15^]: https://www.awwwards.com/Studio375/ — Studio375 profile & awards
|
||||||
|
[^16^]: https://noomoagency.com/ — Noomo Agency honors list (WOTY, Labs awards)
|
||||||
|
[^17^]: https://www.awwwards.com/sites/david-whyte-experience — David Whyte Experience SOTD Dec 31 2024
|
||||||
|
[^18^]: https://gsap.com/community/forums/topic/25188-airpods-image-sequence-animation-using-scrolltrigger/ — master-timeline percentage technique
|
||||||
|
[^19^]: https://css-tricks.com/lets-make-one-of-those-fancy-scrolling-animations-used-on-apple-product-pages/ — Apple flipbook technique + slow-connection fallback
|
||||||
|
[^20^]: https://www.awwwards.com/case-study-david-whyte-experience-by-immersive-garden.html — fluid-sim atlas, baked noise, full stack (Nuxt/Three/GSAP/Lenis/WP/Vercel)
|
||||||
|
[^24^]: https://www.awwwards.com/immersivegarden/ — Immersive Garden profile (Studio of the Year ×3)
|
||||||
|
[^27^]: https://www.awwwards.com/case-study-immersive-gardens-new-website.html — KTX compression, channel packing, gltf-transform; Nuxt/Three/GSAP/Lenis/Strapi/Vercel
|
||||||
|
[^28^]: https://www.awwwards.com/case-study-ai-with-a-human-touch-the-new-face-of-kriss-ai.html — Kriss.ai dollhouse concept; Cloudflare Pages + R2
|
||||||
|
[^35^]: https://www.awwwards.com/sites/kpr — KPR SOTD Dec 26 2022
|
||||||
|
[^36^]: https://www.awwwards.com/sites/hape — HAPE SOTD Apr 27 2022
|
||||||
|
[^37^]: https://www.deptagency.com/case/creating-a-3d-nft-walking-hape/ — HAPE: custom OGL micro-framework, PBR, skinning; 4-week build
|
||||||
|
[^38^]: https://www.awwwards.com/directory/agency-studio/ — Unseen Studio directory stats (35 SOTD)
|
||||||
|
[^39^]: https://x.com/uns__nstudio — Unseen Studio: rebrand from GC, volumetric studies, particle sims, three.js tags
|
||||||
|
[^40^]: https://dogstudio.co/cases/hape-prime/ — HAPE walk concept, scroll camera, soundtrack sync
|
||||||
|
[^41^]: https://kprverse.com/ — KPR live site: terminal loader strings, device gating messages
|
||||||
|
[^45^]: https://www.awwwards.com/kpr-by-resn-wins-sotm.html — KPR by Resn SOTM: click-and-hold layers, tableaux, 3D+2D mix
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
# 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` (0–1). 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.
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Svelte 5 Code Writer
|
||||||
|
|
||||||
|
## CLI Tools
|
||||||
|
|
||||||
|
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
|
||||||
|
|
||||||
|
### List Documentation Sections
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @sveltejs/mcp list-sections
|
||||||
|
```
|
||||||
|
|
||||||
|
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
|
||||||
|
|
||||||
|
### Get Documentation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @sveltejs/mcp get-documentation "<section1>,<section2>,..."
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrieves full documentation for specified sections. Use after `list-sections` to fetch relevant docs.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @sveltejs/mcp get-documentation "$state,$derived,$effect"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Svelte Autofixer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @sveltejs/mcp svelte-autofixer "<code_or_path>" [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
Analyzes Svelte code and suggests fixes for common issues.
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
|
||||||
|
- `--async` - Enable async Svelte mode (default: false)
|
||||||
|
- `--svelte-version` - Target version: 4 or 5 (default: 5)
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Analyze inline code (escape $ as \$)
|
||||||
|
npx @sveltejs/mcp svelte-autofixer '<script>let count = \$state(0);</script>'
|
||||||
|
|
||||||
|
# Analyze a file
|
||||||
|
npx @sveltejs/mcp svelte-autofixer ./src/lib/Component.svelte
|
||||||
|
|
||||||
|
# Target Svelte 4
|
||||||
|
npx @sveltejs/mcp svelte-autofixer ./Component.svelte --svelte-version 4
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
|
||||||
|
2. **Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
|
||||||
|
3. **Always validate** - Run `svelte-autofixer` before finalizing any Svelte component
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
## `$state`
|
||||||
|
|
||||||
|
Only use the `$state` rune for variables that should be _reactive_ — in other words, variables that cause an `$effect`, `$derived` or template expression to update. Everything else can be a normal variable.
|
||||||
|
|
||||||
|
Objects and arrays (`$state({...})` or `$state([...])`) are made deeply reactive, meaning mutation will trigger updates. This has a trade-off: in exchange for fine-grained reactivity, the objects must be proxied, which has performance overhead. In cases where you're dealing with large objects that are only ever reassigned (rather than mutated), use `$state.raw` instead. This is often the case with API responses, for example.
|
||||||
|
|
||||||
|
## `$derived`
|
||||||
|
|
||||||
|
To compute something from state, use `$derived` rather than `$effect`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// do this
|
||||||
|
let square = $derived(num * num);
|
||||||
|
|
||||||
|
// don't do this
|
||||||
|
let square;
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
square = num * num;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE] `$derived` is given an expression, _not_ a function. If you need to use a function (because the expression is complex, for example) use `$derived.by`.
|
||||||
|
|
||||||
|
Deriveds are writable — you can assign to them, just like `$state`, except that they will re-evaluate when their expression changes.
|
||||||
|
|
||||||
|
If the derived expression is an object or array, it will be returned as-is — it is _not_ made deeply reactive. You can, however, use `$state` inside `$derived.by` in the rare cases that you need this.
|
||||||
|
|
||||||
|
## `$effect`
|
||||||
|
|
||||||
|
Effects are an escape hatch and should mostly be avoided. In particular, avoid updating state inside effects.
|
||||||
|
|
||||||
|
- If you need to sync state to an external library such as D3, it is often neater to use [`{@attach ...}`](references/@attach.md)
|
||||||
|
- If you need to run some code in response to user interaction, put the code directly in an event handler or use a [function binding](references/bind.md) as appropriate
|
||||||
|
- If you need to log values for debugging purposes, use [`$inspect`](references/$inspect.md)
|
||||||
|
- If you need to observe something external to Svelte, use [`createSubscriber`](references/svelte-reactivity.md)
|
||||||
|
|
||||||
|
Never wrap the contents of an effect in `if (browser) {...}` or similar — effects do not run on the server.
|
||||||
|
|
||||||
|
## `$props`
|
||||||
|
|
||||||
|
Treat props as though they will change. For example, values that depend on props should usually use `$derived`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// @errors: 2451
|
||||||
|
let { type } = $props();
|
||||||
|
|
||||||
|
// do this
|
||||||
|
let color = $derived(type === 'danger' ? 'red' : 'green');
|
||||||
|
|
||||||
|
// don't do this — `color` will not update if `type` changes
|
||||||
|
let color = type === 'danger' ? 'red' : 'green';
|
||||||
|
```
|
||||||
|
|
||||||
|
## `$inspect.trace`
|
||||||
|
|
||||||
|
`$inspect.trace` is a debugging tool for reactivity. If something is not updating properly or running more than it should you can add `$inspect.trace(label)` as the first line of an `$effect` or `$derived.by` (or any function they call) to trace their dependencies and discover which one triggered an update.
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
Any element attribute starting with `on` is treated as an event listener:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<button onclick={() => {...}}>click me</button>
|
||||||
|
|
||||||
|
<!-- attribute shorthand also works -->
|
||||||
|
<button {onclick}>...</button>
|
||||||
|
|
||||||
|
<!-- so do spread attributes -->
|
||||||
|
<button {...props}>...</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to attach listeners to `window` or `document` you can use `<svelte:window>` and `<svelte:document>`:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<svelte:window onkeydown={...} />
|
||||||
|
<svelte:document onvisibilitychange={...} />
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid using `onMount` or `$effect` for this.
|
||||||
|
|
||||||
|
## Snippets
|
||||||
|
|
||||||
|
[Snippets](references/snippet.md) are a way to define reusable chunks of markup that can be instantiated with the [`{@render ...}`](references/@render.md) tag, or passed to components as props. They must be declared within the template.
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
{#snippet greeting(name)}
|
||||||
|
<p>hello {name}!</p>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{@render greeting('world')}
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE] Snippets declared at the top level of a component (i.e. not inside elements or blocks) can be referenced inside `<script>`. A snippet that doesn't reference component state is also available in a `<script module>`, in which case it can be exported for use by other components.
|
||||||
|
|
||||||
|
## Each blocks
|
||||||
|
|
||||||
|
Prefer to use [keyed each blocks](references/each.md) — this improves performance by allowing Svelte to surgically insert or remove items rather than updating the DOM belonging to existing items.
|
||||||
|
|
||||||
|
> [!NOTE] The key _must_ uniquely identify the object. Do not use the index as a key.
|
||||||
|
|
||||||
|
Avoid destructuring if you need to mutate the item (with something like `bind:value={item.count}`, for example).
|
||||||
|
|
||||||
|
## Using JavaScript variables in CSS
|
||||||
|
|
||||||
|
If you have a JS variable that you want to use inside CSS you can set a CSS custom property with the `style:` directive.
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<div style:--columns={columns}>...</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then reference `var(--columns)` inside the component's `<style>`.
|
||||||
|
|
||||||
|
## Styling child components
|
||||||
|
|
||||||
|
The CSS in a component's `<style>` is scoped to that component. If a parent component needs to control the child's styles, the preferred way is to use CSS custom properties:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<!-- Parent.svelte -->
|
||||||
|
<Child --color="red" />
|
||||||
|
|
||||||
|
<!-- Child.svelte -->
|
||||||
|
<h1>Hello</h1>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
color: var(--color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
If this is impossible (for example, the child component comes from a library) you can use `:global` to override styles:
|
||||||
|
|
||||||
|
```svelte
|
||||||
|
<div>
|
||||||
|
<Child />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div :global {
|
||||||
|
h1 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Consider using context instead of declaring state in a shared module. This will scope the state to the part of the app that needs it, and eliminate the possibility of it leaking between users when server-side rendering.
|
||||||
|
|
||||||
|
Use `createContext` rather than `setContext` and `getContext`, as it provides type safety.
|
||||||
|
|
||||||
|
## Async Svelte
|
||||||
|
|
||||||
|
If using version 5.36 or higher, you can use [await expressions](references/await-expressions.md) and [hydratable](references/hydratable.md) to use promises directly inside components. Note that these require the `experimental.async` option to be enabled in `svelte.config.js` as they are not yet considered fully stable.
|
||||||
|
|
||||||
|
## Avoid legacy features
|
||||||
|
|
||||||
|
Always use runes mode for new code, and avoid features that have more modern replacements:
|
||||||
|
|
||||||
|
- use `$state` instead of implicit reactivity (e.g. `let count = 0; count += 1`)
|
||||||
|
- use `$derived` and `$effect` instead of `$:` assignments and statements (but only use effects when there is no better solution)
|
||||||
|
- use `$props` instead of `export let`, `$$props` and `$$restProps`
|
||||||
|
- use `onclick={...}` instead of `on:click={...}`
|
||||||
|
- use `{#snippet ...}` and `{@render ...}` instead of `<slot>` and `$$slots` and `<svelte:fragment>`
|
||||||
|
- use `<DynamicComponent>` instead of `<svelte:component this={DynamicComponent}>`
|
||||||
|
- use `import Self from './ThisComponent.svelte'` and `<Self>` instead of `<svelte:self>`
|
||||||
|
- use classes with `$state` fields to share reactivity between components, instead of using stores
|
||||||
|
- use `{@attach ...}` instead of `use:action`
|
||||||
|
- use clsx-style arrays and objects in `class` attributes, instead of the `class:` directive
|
||||||
|
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
# GUIDE COMPLET : Licences IA pour un Factotum Numérique
|
||||||
|
## Visual Novel Ren'Py · Clips vidéo · Films d'animation · Jeux Godot · Sites web · Apps web
|
||||||
|
|
||||||
|
**Date :** 2026-07-23
|
||||||
|
**Profil :** Créateur indépendant multi-casquettes (2D/3D/web/adminsys/support/formation), Guadeloupe, éthique libriste et souveraineté numérique, budget contraint.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Ce que les autres LLM t'ont dit de FAUX (corrections d'abord)
|
||||||
|
|
||||||
|
Les deux documents que tu as collectés contiennent des erreurs factuelles importantes. Avant d'aller plus loin, corrigeons :
|
||||||
|
|
||||||
|
### Erreur n°1 — Anthropic « conserve les droits » / « licence d'usage, pas transfert »
|
||||||
|
**Faux.** Les Commercial Terms d'Anthropic assignent la propriété des outputs au client (« As between you and Anthropic, you own the Outputs »), exactement comme OpenAI. Le tableau qui affirme le contraire est une hallucination classique : les LLM confondent la *licence d'utilisation du service* avec la *propriété des outputs*. Idem pour Google : les Gemini API Additional Terms disent explicitement « Google does not claim ownership of generated content ».
|
||||||
|
|
||||||
|
### Erreur n°2 — « Claude via OpenRouter n'est pas couverte par les accords standards »
|
||||||
|
**Infondé.** OpenRouter est un agrégateur légitime qui reverse les requêtes aux fournisseurs. Les ToS du fournisseur sous-jacent s'appliquent (c'est vrai), mais il n'y a aucune clause Anthropic interdisant le routage via OpenRouter. Ce qui compte : tu es lié par les Usage Policies d'Anthropic sur le *contenu*, pas sur le *canal*.
|
||||||
|
|
||||||
|
### Erreur n°3 — « Pour un VN commercial, évitez les images générées par IA »
|
||||||
|
**Excessif.** La vraie distinction n'est pas « IA = interdit » mais :
|
||||||
|
- **Permission d'exploiter** → accordée par la plupart des plans payants (Midjourney, Runway, Kling, Suno Pro, etc.) et totale sur les modèles open-weight auto-hébergés.
|
||||||
|
- **Protection par le droit d'auteur** → c'est là le vrai problème : un output 100 % IA n'est pas protégeable en France/UE (voir §7). Tu peux l'exploiter, mais tu ne peux pas empêcher un tiers de le copier.
|
||||||
|
|
||||||
|
### Erreur n°4 — « Tribunal de Paris, 2023 »
|
||||||
|
**Date fausse.** La décision de référence est du Tribunal judiciaire de Paris, **25 octobre 2022**. Et le jalon le plus récent est le **Tribunal régional de Munich, 13 février 2026** (n° 142 C 9786/25) : trois logos générés par IA refusés de protection faute d'« influence créatrice humaine prépondérante ». Raisonnement transposable en droit français (arrêts *Cofemel*, *BSA*, *Mio* de la CJUE).
|
||||||
|
|
||||||
|
### Erreur n°5 — Modèles cités obsolètes
|
||||||
|
GPT-4o, Claude 3.5, Grok 2/3, Llama 3.1, Qwen 2.5… On est en juillet 2026. Les familles actuelles : Kimi K2.5/K3 (open-weight, Modified MIT), Llama 4, Qwen 3, DeepSeek V3.x/R1+, Mistral Large 3 / Magistral, Claude 4.x, GPT-5.x, Gemini 3. Les conclusions par *famille de licence* restent valables, mais vérifie toujours la licence exacte de la version précise que tu utilises.
|
||||||
|
|
||||||
|
### Erreur n°6 — ComfyUI GPL v3 : formulation trompeuse
|
||||||
|
ComfyUI est bien GPL-3.0, mais :
|
||||||
|
- Les **outputs générés** (images, vidéos) ne sont **pas** couverts par la GPL. Un programme GPL ne contamine pas ses outputs.
|
||||||
|
- Les **workflows JSON** ne sont pas des œuvres dérivées du code : tu les distribues comme tu veux.
|
||||||
|
- Seuls les **custom nodes Python que tu redistribues** et qui dérivent du code ComfyUI doivent respecter la GPL. Ce qui est normal et cohérent avec ton éthique libriste de toute façon.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Le framework : 4 couches de risque, pas une
|
||||||
|
|
||||||
|
Pour chaque travail que tu produis, il faut vérifier 4 couches indépendantes :
|
||||||
|
|
||||||
|
| Couche | Question | Exemple de réponse |
|
||||||
|
|---|---|---|
|
||||||
|
| **1. Outil** | La licence du logiciel autorise-t-elle l'usage commercial ? | Ren'Py MIT ✅, Godot MIT ✅, ComfyUI GPL-3.0 ✅ (outputs libres), Blender GPL ✅ (outputs libres) |
|
||||||
|
| **2. Modèle** | Les ToS/licence du modèle autorisent-ils l'exploitation commerciale de l'output ? | Apache 2.0 / MIT → oui sans condition. API propriétaire → oui sur plan payant généralement. Gratuit → souvent non. |
|
||||||
|
| **3. Contenu** | L'output respecte-t-il les politiques de contenu (NSFW, marques, personnes réelles) ? | Tous les providers propriétaires interdisent le NSFW explicite. Aucun filtre en self-hosted. |
|
||||||
|
| **4. Protection** | L'output est-il protégeable par le droit d'auteur ? | Uniquement si apport créatif humain prépondérant et documentable. |
|
||||||
|
|
||||||
|
**Règle d'or du factotum :** un asset peut passer les couches 1-3 et échouer la couche 4. C'est le piège central de tout travail commercial avec clients : si tu vends un livrable avec garantie de propriété intellectuelle, la couche 4 devient contractuelle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Tableau de référence — Familles de modèles (état juillet 2026)
|
||||||
|
|
||||||
|
### 2.1 LLM (texte : scénarios, dialogues, code, doc, traduction)
|
||||||
|
|
||||||
|
| Famille | Licence/accès | Output commercial | Entraînement sur tes données | Risque | Verdict factotum |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| **Kimi K2.5/K3** (Moonshot) | Open-weight, Modified MIT + API | ✅ Oui (poids ouverts : liberté totale ; API : voir ToS) | API : voir politique ; self-hosted : non | 🟢 Faible en self-hosted | 🟢 Excellent rapport qualité/prix, poids ouverts = souveraineté |
|
||||||
|
| **DeepSeek V3.x / R1+** | Open-weight MIT | ✅ Oui | Self-hosted : non | 🟢 Faible | 🟢 Recommandé |
|
||||||
|
| **Qwen 3** (Alibaba) | Apache 2.0 (plupart des variantes) | ✅ Oui | Self-hosted : non | 🟢 Faible | 🟢 Recommandé (et tes pipelines Qwen-image-edit en bénéficient) |
|
||||||
|
| **Llama 4** (Meta) | Llama Community License | ✅ Oui (sauf >700M MAU, te concerne pas) | Self-hosted : non | 🟢 Faible | 🟢 OK, licence un peu plus restrictive qu'Apache |
|
||||||
|
| **Mistral Large / Magistral** | Apache 2.0 (certains) / MRL (recherche) | ⚠️ Vérifier modèle par modèle | Self-hosted : non | 🟢 Faible | 🟢 OK si variante Apache |
|
||||||
|
| **GPT-5.x** (OpenAI API) | Propriétaire | ✅ Assignés à l'utilisateur | API : non par défaut | 🟡 Modéré (contenu NSFW 🔴) | 🟡 Brainstorming, code, doc |
|
||||||
|
| **Claude 4.x** (Anthropic API) | Propriétaire | ✅ Assignés à l'utilisateur | API : non par défaut | 🟡 Modéré (contenu NSFW 🔴) | 🟡 Idem |
|
||||||
|
| **Gemini 3** (Google) | Propriétaire / API | ✅ Google ne revendique pas la propriété | API/Workspace : non ; gratuit : oui | 🟡 Modéré (NSFW 🔴) | 🟡 Idem |
|
||||||
|
| **Grok 4** (xAI) | Propriétaire / API | ✅ Assignés | Voir ToS | 🟡 Modéré (ToS changeantes) | 🟡 Tolérance contenu plus large, ToS à surveiller |
|
||||||
|
|
||||||
|
### 2.2 Image (sprites, backgrounds, concept art, affiches, logos, textures)
|
||||||
|
|
||||||
|
| Modèle/outil | Licence | Commercial | Note factotum |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Flux.1 [dev/schnell]** (Black Forest) | Apache 2.0 (schnell) / licence non-commerciale (dev) | schnell ✅ / dev ⚠️ | Le plus propre en local pour du commercial : **schnell** |
|
||||||
|
| **Qwen-Image / Qwen-Image-Edit** | Apache 2.0 | ✅ Oui | 🟢 Ton pipeline existant, licence clean |
|
||||||
|
| **SD 1.5 / SDXL** | CreativeML Open RAIL-M | ✅ **Sans seuil de revenus** | 🟢 Anciens mais juridiquement les plus clean de chez Stability |
|
||||||
|
| **Stable Diffusion 3 / 3.5** | Stability Community License | ✅ <1M$ CA ; au-delà → Enterprise | Seuil de revenu comme Midjourney |
|
||||||
|
| **Seedream 4.x** (ByteDance) | Propriétaire (API/app) | ⚠️ Voir ToS plan | Qualité top, mais juridiquement opaque — éviter pour livrables clients |
|
||||||
|
| **Nano Banana Pro** (Google) | Propriétaire (Gemini) | ✅ Plan payant | Bon outil, Google n'accapare pas les outputs |
|
||||||
|
| **Midjourney** | Propriétaire | ✅ Payant uniquement ; >1M$ CA → plan Pro/Mega | Gratuit = ❌ commercial (CC BY-NC). **Aucune indemnisation** en cas de litige copyright. Litige *Disney/Universal v. Midjourney* (juin 2025) en cours → politiques susceptibles de changer |
|
||||||
|
| **DALL-E 3 / gpt-image** (OpenAI) | Propriétaire | ✅ Assignés | OK |
|
||||||
|
|
||||||
|
### 2.3 Vidéo (clips, films d'animation, cinématiques de jeu)
|
||||||
|
|
||||||
|
| Modèle | Licence | Commercial | Note |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Wan 2.1 / 2.2** (Alibaba) | **Apache 2.0** | ✅ Sans restriction | 🟢 **La référence souveraine** pour tes clips/animations en local (24 Go VRAM en quantifié) |
|
||||||
|
| **LTX-Video / LTX-2.3** (Lightricks) | Duale : Community/Commercial | ⚠️ Community gratuite sous seuil | Très rapide, audio intégré sur 2.3 |
|
||||||
|
| **HunyuanVideo 1.5** (Tencent) | Apache 2.0 (1.5) / Community License (base : <100M MAU) | ✅ | OK en local |
|
||||||
|
| **Mochi 1 / CogVideoX** | Apache 2.0 | ✅ | Plus anciens mais clean |
|
||||||
|
| **Runway Gen-4.5** | Propriétaire | ✅ Plan payant (12$/mois+) ; gratuit = non commercial | ⚠️ **Runway entraîne sur tes inputs/outputs par défaut sur tous les plans sauf Enterprise** — pas d'assets client sans accord. Outputs marqués C2PA (provenance IA lisible par les plateformes) |
|
||||||
|
| **Kling 3.0** (Kuaishou) | Propriétaire | ✅ Plan payant | Audio-vidéo natif, tarifs en RMB |
|
||||||
|
| **Veo 3.1** (Google) | Propriétaire | ✅ Plan payant (~29$/mois) | Qualité cinéma, jusqu'à 120 s |
|
||||||
|
| **Sora 2** (OpenAI) | Propriétaire | ✅ Plan payant | Restrictions contenu strictes |
|
||||||
|
| **Seedance 2.0** (ByteDance) | Propriétaire | ⚠️ Accès/tarifs opaques | Frontier mais juridiquement flou |
|
||||||
|
|
||||||
|
### 2.4 Musique & son (OST de jeux, clips, génériques)
|
||||||
|
|
||||||
|
| Outil | Licence | Commercial | Alerte |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Suno** (v5.x) | Propriétaire | ✅ Pro 10$/mois+, ❌ gratuit | Litige Sony en cours (jugement été 2026) ; Suno ne garantit **pas** que le copyright s'attache à l'output ; tu accordes à Suno une licence perpétuelle sur tes inputs |
|
||||||
|
| **Udio** | Propriétaire | ⚠️ **À ÉVITER** | Depuis l'accord UMG (oct. 2025) : **téléchargements désactivés**, walled garden streaming-only. Inutilisable pour livrer des fichiers |
|
||||||
|
| **ACE-Step / Stable Audio Open** | Open-weight | ✅ | 🟢 Voie souveraine, qualité en progrès |
|
||||||
|
| **Banques libres** (Musopen, Incompetech, Freesound CC0) | CC0/CC-BY | ✅ | 🟢 Zéro risque, créditer si CC-BY |
|
||||||
|
| **SFX générés** (audio_generation, ElevenLabs SFX…) | Selon provider | ✅ généralement | Vérifier ToS plan |
|
||||||
|
|
||||||
|
### 2.5 Voix / TTS (doublage, narration, créole)
|
||||||
|
|
||||||
|
| Outil | Commercial | Alerte |
|
||||||
|
|---|---|---|
|
||||||
|
| **Voix clonées** (n'importe quel outil) | — | 🔴 Clone uniquement TA voix ou avec consentement écrit. Le Danemark a ouvert la voie à un droit de la voix/à l'image contre les deepfakes ; la France suit (droit à l'image déjà applicable) |
|
||||||
|
| **ElevenLabs** | ✅ Payant | Gratuit : attribution obligatoire |
|
||||||
|
| **Piper / Coqui XTTS / Kokoro** (local) | ✅ | 🟢 Souverain, licences open |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Analyse par type de production
|
||||||
|
|
||||||
|
### 3.1 Visual Novel Ren'Py
|
||||||
|
- **Moteur :** MIT, zéro problème, commercial OK.
|
||||||
|
- **Texte :** idéation avec n'importe quel LLM → réécriture/finalisation humaine (résout la couche 4 et le style). Pour un VN **adulte** : aucun provider propriétaire ne générera le NSFW final → self-hosted (Kimi K2.5, Qwen 3, DeepSeek) ou LLM locaux via Ollama/llama.cpp.
|
||||||
|
- **Sprites/backgrounds :** pipeline ComfyUI + Qwen-Image-Edit (Apache 2.0) + retouches humaines systématiques (paint-over dans Krita) → c'est le paint-over qui crée l'apport créatif humain de la couche 4.
|
||||||
|
- **Publication Steam :** déclaration IA **obligatoire** pour tout asset IA visible par le joueur (art, audio, texte, code shippé, assets marketing). Les outils de dev (Copilot, concept art non shippé) sont exemptés depuis la réécriture du 17 janvier 2026. Contenu sexuel généré par IA *en live* pendant le gameplay = interdiction absolue.
|
||||||
|
- **Publication itch.io :** tag « AI-Generated » obligatoire (graphismes/sons/texte/code), sous peine de désindexation — même si retouché ensuite.
|
||||||
|
- **NSFW Ren'Py :** itch.io tolère l'adulte tagué ; Steam exige le patch externe. Juridiquement, le NSFW auto-hébergé ne pose pas de problème de licence, uniquement de plateforme et de droit pénal (rien d'illégal : pas de mineurs, même fictifs, selon les juridictions visées).
|
||||||
|
|
||||||
|
### 3.2 Clips vidéo
|
||||||
|
- **Chaîne sûre :** Wan 2.2 local (Apache 2.0) → montage DaVinci Resolve/Kdenlive → musique Suno Pro (si tu acceptes le risque Sony) ou banque libre → YouTube.
|
||||||
|
- **YouTube :** pas d'interdiction IA, mais label « contenu modifié/synthétique » obligatoire pour les contenus réalistes synthétiques ; monétisation possible si contenu transformé/original (les fermes à slop non éditées sont démonétisées depuis juillet 2025, politique « inauthentic content »).
|
||||||
|
- **Client (clip pour un tiers) :** ⚠️ Suno = licence non-exclusive, pas de garantie de copyright → ne jamais vendre une « exclusivité » sur une musique 100 % Suno. Pour la synchro client, privilégier banques de musique à licence claire ou composition assistée + production humaine documentée.
|
||||||
|
|
||||||
|
### 3.3 Films d'animation
|
||||||
|
- Cumul de tous les risques : image (style cohérent → LoRA entraînés localement sur TES designs, ce qui renforce la couche 4), vidéo (Wan/LTX local), voix (TTS local ou comédiens), musique (id. ci-dessus).
|
||||||
|
- **Point critique — festivals et distributeurs :** de plus en plus demandent une déclaration des usages IA. Un dossier de création documenté (prompts, itérations, retouches) devient un **argument de vente**, pas juste une protection.
|
||||||
|
- **Personnages :** un design de personnage validé par itérations + model sheet dessinée/retouchée par toi = personnage protégeable ; un prompt unique = personnage non protégeable que n'importe qui peut réutiliser.
|
||||||
|
|
||||||
|
### 3.4 Jeux vidéo Godot
|
||||||
|
- **Moteur :** MIT, attribution appréciée mais non obligatoire, commercial OK, exports toutes plateformes libres.
|
||||||
|
- **Code GDScript/C# généré par IA :** pas de restriction de licence sur le code produit par un LLM (c'est un output comme un autre) ; mais sur Steam, du code 100 % IA *shippé* est déclarable. Les assistants de code (aide à l'écriture) sont exemptés.
|
||||||
|
- **Assets :** mêmes règles que le VN. Bonus Godot : l'écosystème d'assets libres (Kenney CC0, OpenGameArt) est immense et zéro risque.
|
||||||
|
- **Clone Cannon Fodder :** attention, licence du *code* OK mais le **nom, les sprites, la musique originaux sont propriété de Sensible Software/Codemasters** → faire un « inspired by », pas un clone d'assets.
|
||||||
|
|
||||||
|
### 3.5 Sites web & applications web (y compris pour clients : communes, associations, KBM…)
|
||||||
|
- **Le moins risqué de tes domaines.** Le code généré par LLM ne pose pas de problème de licence (pas de « licence virale » sur les outputs).
|
||||||
|
- **Vrai risque n°1 : RGPD et contenu client.** Si tu passes des données d'une collectivité (Grand Sud Caraïbe) dans une API LLM : données personnelles → API avec engagement de non-entraînement (toutes les API payantes le garantissent) ou self-hosted. Le gratuit grand public (Gemini free, etc.) entraîne par défaut.
|
||||||
|
- **Vrai risque n°2 : garanties contractuelles.** Si ton devis cède la propriété intellectuelle au client, tout output 100 % IA non retravaillé fragilise la cession (on ne cède pas ce qu'on ne possède pas). Solution : retravail humain + clause décrivant l'usage d'outils IA.
|
||||||
|
- **Vrai risque n°3 : accessibilité/conformité** (RGAA pour le public français) — hors IA mais c'est toi le responsable en tant que factotum.
|
||||||
|
- **Stack libriste :** SvelteKit (MIT), HTMX (BSD), Tailwind (MIT), Strapi (MIT avec limites EE), Authentik (MIT), 11ty (MIT) — tout est clean. Les dépendances npm : vérifier les licences via `license-checker` dans la CI, une habitude qui coûte 5 minutes.
|
||||||
|
|
||||||
|
### 3.6 Modélisation 3D (assets Godot, animation, print)
|
||||||
|
- Domaine le moins stabilisé juridiquement : les ToS des générateurs 3D (Meshy, Tripo, Luma Genie) sont souvent vagues sur la propriété des meshes générés ; les plans gratuits sont quasi toujours non-commerciaux.
|
||||||
|
- **Voie sûre :** génération 3D locale (TripoSG, Hunyuan3D 2.x — open-weight) → retopologie/UV/textures retravaillées dans Blender (GPL, outputs libres). Comme pour la 2D : la retouche humaine crée la couche 4.
|
||||||
|
- **Assets critiques pour un jeu commercial :** privilégier banques à licence claire (Kenney CC0, Quaternius, Poly Pizza CC) ou modélisation propre ; la 3D générée en appoint, jamais en exclusivité vendue.
|
||||||
|
|
||||||
|
### 3.7 Formation / support / adminsys
|
||||||
|
- Supports de formation rédigés avec LLM : OK, mais relire (hallucinations = responsabilité pro).
|
||||||
|
- Captures/screenshots dans tes supports : droit des marques et des interfaces — usage pédagogique toléré, citation de source.
|
||||||
|
- Scripts d'automatisation générés : aucune restriction.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. La couche 4 en profondeur : protéger tes œuvres en droit français
|
||||||
|
|
||||||
|
État du droit (France/UE, juillet 2026) :
|
||||||
|
- **Œuvre 100 % IA** (prompt générique, pas de retouche) → pas d'œuvre de l'esprit, **pas de protection**. TJ Paris 25 oct. 2022 ; Tribunal de Munich 13 fév. 2026 ; US Copyright Office janv. 2025 (*Thaler v. Perlmutter* confirmé).
|
||||||
|
- **Œuvre assistée par IA** (direction créative humaine prépondérante : prompts itératifs, sélection, recomposition, retouche, intégration dans un ensemble plus vaste) → **protégeable**, l'auteur étant l'humain. C'est la « théorie de l'utilisateur-auteur ».
|
||||||
|
- Munich 2026 précise : ni le temps passé, ni la longueur du prompt, ni l'abonnement payant ne suppléent l'originalité. Ce qui compte : des **choix libres et créatifs** documentables.
|
||||||
|
|
||||||
|
### Protocole « preuve d'apport humain » (à automatiser, coût quasi nul)
|
||||||
|
1. **Archive les prompts** et leurs versions (ComfyUI les embarque dans les PNG metadata — conserve-les).
|
||||||
|
2. **Garde les itérations intermédiaires** (drafts, inpaints successifs).
|
||||||
|
3. **Retouche systématique** : paint-over Krita, re-timing montage, réécriture dialogues — 10 % de modification humaine visible vaut mieux que 0 %.
|
||||||
|
4. **Horodate** : git pour textes/code, dossiers datés pour assets, éventuellement e-horodatage/blockchain timestamp pour les œuvres stratégiques (logo AKILPA, personnages récurrents).
|
||||||
|
5. **Pour les logos et identités (AKILPA, Denkyem X, clients) :** 🔴 ne jamais livrer un logo 100 % IA brut. Un logo non protégeable + non déposable en marque = catastrophe pour une association ou un client. Workflow : IA pour l'exploration → vectorisation et refonte manuelle dans Inkscape → dépôt INPI éventuel en ton nom/ton association.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Matrice de décision rapide
|
||||||
|
|
||||||
|
| Situation | Choix recommandé |
|
||||||
|
|---|---|
|
||||||
|
| Brainstorming, idéation, specs, doc | N'importe quel LLM, même propriétaire — rien ne ship |
|
||||||
|
| Texte final commercial (jeu, site, formation) | Réécriture humaine OU LLM open-weight (Kimi K2.5/Qwen3/DeepSeek) |
|
||||||
|
| Code (Ren'Py, Godot, web) | LLM quelconque, review humaine — risque minimal |
|
||||||
|
| Assets image commerciaux | ComfyUI local + Apache 2.0 (Qwen-Image, Flux schnell) + paint-over |
|
||||||
|
| Vidéo/animation commerciale | Wan 2.2 / HunyuanVideo local ; Runway/Kling payant si délai court |
|
||||||
|
| Musique de fond (jeu, clip perso) | Suno Pro (risque Sony assumé) ou banque libre |
|
||||||
|
| Musique pour client / synchro | Banque à licence claire ou prod assistée documentée — jamais d'exclusivité sur du 100 % IA |
|
||||||
|
| NSFW (VN adulte) | Self-hosted uniquement ; jamais d'API propriétaire |
|
||||||
|
| Données clients/collectivités | API payante (no-training) ou local — jamais de tier gratuit |
|
||||||
|
| Logo / identité de marque | IA = exploration ; livrable = refonte vectorielle humaine |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Check-list avant chaque livraison
|
||||||
|
|
||||||
|
- [ ] Licence du modèle exact (version précise) vérifiée sur le repo/page officiel — pas d'après un tableau de blog
|
||||||
|
- [ ] Plan payant actif au moment de la génération si modèle propriétaire (les droits s'apprécient à la date de génération — archive les factures)
|
||||||
|
- [ ] Output retouché + itérations archivées (couche 4)
|
||||||
|
- [ ] Déclaration IA prévue : Steam (survey), itch.io (tag), YouTube (label), client (clause devis)
|
||||||
|
- [ ] Mentions légales / LICENSE.txt à jour (moteur, assets tiers, polices SIL OFL, musiques CC)
|
||||||
|
- [ ] Pas de marque, personnage, voix ou image de personne réelle dans les prompts sans droit
|
||||||
|
- [ ] Pour clients : clause IA dans le devis (outils utilisés, garanties, cession limitée à ce qui est cessible)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Position stratégique (ton profil libriste)
|
||||||
|
|
||||||
|
Ta contrainte budgétaire (40-50 €/mois) et ton éthique convergent vers la même architecture :
|
||||||
|
|
||||||
|
1. **Local d'abord** : ComfyUI + Qwen-Image-Edit + Wan 2.2 + LLM local (Kimi K2.5 / Qwen 3 via Ollama) = 0 €/mois, souveraineté totale, aucune ToS de contenu, pas de fuite de données client.
|
||||||
|
2. **API propriétaires en appoint** : OpenRouter crédité à l'usage pour le brainstorming haut de gamme et les modèles frontier — jamais pour les outputs finaux sensibles.
|
||||||
|
3. **SaaS payants ciblés** : un seul à la fois selon le projet du moment (Suno Pro 10 $ pour un mois de prod musicale, Runway 12 $ pour un clip) — tu n'as pas besoin d'abonnements permanents.
|
||||||
|
4. **La documentation comme actif** : ton dossier de création vaut juridiquement plus que n'importe quel abonnement.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Modèle de LICENSE.txt enrichi (tous projets)
|
||||||
|
|
||||||
|
Version élargie avec clause proactive « Génération par IA » — la transparence documentée joue en ta faveur (couche 4) au lieu de te fragiliser :
|
||||||
|
|
||||||
|
```text
|
||||||
|
=========================================================
|
||||||
|
[NOM DU PROJET] - MENTIONS LÉGALES ET LICENCES
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
MOTEUR / PLATEFORME
|
||||||
|
[Ex: Ren'Py — MIT © Tom Rothamel / Godot — MIT / SvelteKit — MIT]
|
||||||
|
|
||||||
|
CONTENU ORIGINAL
|
||||||
|
Le scénario, les dialogues, la programmation, la direction artistique et le montage
|
||||||
|
sont l'œuvre de [TON NOM / STRUCTURE].
|
||||||
|
Licence du projet : [Tous Droits Réservés / CC BY-NC 4.0 / ...]
|
||||||
|
|
||||||
|
GÉNÉRATION PAR IA (DÉCLARATION)
|
||||||
|
Certains éléments de ce projet ont été produits avec l'assistance d'outils
|
||||||
|
d'intelligence artificielle, puis sélectionnés, modifiés, retouchés et arrangés
|
||||||
|
par l'auteur humain, dont les choix créatifs sont prépondérants.
|
||||||
|
Outils utilisés : [liste — ex: Qwen-Image-Edit (Apache 2.0, local), Wan 2.2
|
||||||
|
(Apache 2.0, local), modèles LLM open-weight auto-hébergés]
|
||||||
|
Éléments concernés : [sprites, backgrounds, cinématiques, musique, ...]
|
||||||
|
|
||||||
|
RESSOURCES TIERCES
|
||||||
|
- [Asset / musique / police] : [Auteur] — [Licence exacte + lien]
|
||||||
|
- Polices : [SIL Open Font License si applicable]
|
||||||
|
|
||||||
|
CONTACT
|
||||||
|
[Email / Site]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Veille contentieuse : les jugements qui peuvent rebattre les cartes
|
||||||
|
|
||||||
|
Les ToS évoluent sous la pression des litiges. À surveiller (revue trimestrielle suffit) :
|
||||||
|
|
||||||
|
- ***Disney/Universal v. Midjourney*** (juin 2025) : si les studios gagnent, les modèles entraînés sur des œuvres protégées pourraient voir leurs politiques commerciales restreintes.
|
||||||
|
- ***Bartz v. Anthropic*** et les actions collectives contre les entraînements : impactent les clauses d'indemnisation et de données.
|
||||||
|
- **Litiges musique** (majors v. Suno) : pourraient imposer des redevances ou restreindre l'export des outputs.
|
||||||
|
- **Évolution EU AI Act** : obligations de transparence sur les contenus synthétiques (marquage) — déjà anticipé par le C2PA de Runway/Google ; prévois-le dans ta chaîne.
|
||||||
|
|
||||||
|
**Réflexe :** avant chaque nouveau projet, revérifier la ToS du modèle exact *à la date de génération*, et archiver un PDF de la version applicable (les ToS s'apprécient au jour de la génération).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Document d'information stratégique, pas un avis juridique. Pour un contrat client à enjeu (collectivité, éditeur) ou un dépôt de marque, validation par un avocat en PI — coût d'une consultation ciblée : 150-300 €, rentabilisé dès le premier litige évité.*
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# PRD — « veille-ia » : observatoire souverain des modèles génératifs
|
||||||
|
## Application YunoHost custom (SvelteKit) pour cyber-mawonaj.net
|
||||||
|
|
||||||
|
**Version :** 1.0 — 1er août 2026
|
||||||
|
**Porteur :** factotum numérique indépendant (Guadeloupe)
|
||||||
|
**Contraintes :** 100 % open-source (licences OSI uniquement), self-hosted YunoHost, ~0 €/mois marginal, pas de dépendance SaaS obligatoire.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Problème à résoudre
|
||||||
|
|
||||||
|
Les modèles génératifs (image, vidéo, SVG, LLM, audio) et leurs **licences** changent toutes les 72 h. Le porteur produit dans 5 contextes aux contraintes juridiques différentes (VN Ren'Py CC BY-NC-ND, vente d'images, clips vendus, jeux Steam/itch.io, diffusion libre). La veille manuelle est chronophage ; aucun outil existant ne fait la boucle complète **surveiller → analyser → recommander selon le besoin → adapter les workflows**.
|
||||||
|
|
||||||
|
## 2. Vision
|
||||||
|
|
||||||
|
Un observatoire personnel qui :
|
||||||
|
1. **Surveille** automatiquement sorties de modèles, changements de licence/ToS, leaderboards et changelog ComfyUI ;
|
||||||
|
2. **Analyse** chaque événement avec un LLM local (classification structurée, pas de résumé vague) ;
|
||||||
|
3. **Maintient un registre** des modèles (source de vérité unique, versionnée git) ;
|
||||||
|
4. **Recommande** des modèles en croisant le registre avec des profils de projets à contraintes ;
|
||||||
|
5. **Régénère** les documents (guides markdown) et **adapte** les workflows ComfyUI paramétriques sans réinventer le graphe.
|
||||||
|
|
||||||
|
## 3. Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ cyber-mawonaj.net (YunoHost) ─────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ changedetection_ynh ──webhook──┐ │
|
||||||
|
│ (surveillance pages licences, │ │
|
||||||
|
│ leaderboards, blogs) ▼ │
|
||||||
|
│ nodered_ynh (Apache 2.0, ~100 Mo) │
|
||||||
|
│ flows: ingestion RSS, appel LLM, │
|
||||||
|
│ routage, notifications │
|
||||||
|
│ │ ▲ │
|
||||||
|
│ HTTP/classifier │ │ HTTP /api/alerts │
|
||||||
|
│ ▼ │ │
|
||||||
|
│ Ollama (local machine ou VPS — voir §8.4) │
|
||||||
|
│ Qwen3-32B / Kimi K2.5 — classifieur JSON structuré │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ★ veille-ia (APP CUSTOM — ce projet) │
|
||||||
|
│ SvelteKit + adapter-node, service systemd, SSO YunoHost │
|
||||||
|
│ - Registre modèles (YAML + git via Gitea_ynh) │
|
||||||
|
│ - Profils projets (YAML) │
|
||||||
|
│ - Moteur de recommandation │
|
||||||
|
│ - Inbox alertes │
|
||||||
|
│ - Exporteur de documents markdown │
|
||||||
|
│ - (Phase 2) Templates ComfyUI paramétriques + smoke tests │
|
||||||
|
│ │
|
||||||
|
│ FreshRSS_ynh (flux RSS sources) · Gitea_ynh (versioning) │
|
||||||
|
└────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Principe d'intégration YunoHost :** toutes les briques périphériques existent dans le catalogue officiel — ne rien ré-empaqueter. Le projet ne package **que** l'app `veille-ia`.
|
||||||
|
|
||||||
|
## 4. Modules fonctionnels de l'app
|
||||||
|
|
||||||
|
### 4.1 Registre des modèles (MVP)
|
||||||
|
CRUD sur `data/registre_modeles.yaml`, versionné git (commit à chaque modification, push optionnel vers Gitea_ynh).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- id: krea-2-turbo
|
||||||
|
nom: Krea 2 Turbo
|
||||||
|
editeur: Krea AI
|
||||||
|
modalites: [image]
|
||||||
|
licence:
|
||||||
|
nom: Krea 2 Community License
|
||||||
|
url: https://huggingface.co/krea/Krea-2-Turbo
|
||||||
|
commercial_ok: true
|
||||||
|
seuil: "équipes <50 sièges"
|
||||||
|
attribution_requise: false
|
||||||
|
poids_ouverts: true
|
||||||
|
garde_fous_obligatoires: true # anti-NCII/CSAM
|
||||||
|
capacites:
|
||||||
|
nsfw_ok: true # en local, sous réserve garde-fous licence
|
||||||
|
vram_gb: 12 # fp8
|
||||||
|
resolution_max: "2K"
|
||||||
|
comfyui_natif: true # ≥ 0.25.0
|
||||||
|
licence_outputs: "commercialisables"
|
||||||
|
scores:
|
||||||
|
arena_image: 6 # rang Artificial Analysis, juin 2026
|
||||||
|
statut: production # production | early-access | annonce | mort
|
||||||
|
derniere_verif: 2026-08-01
|
||||||
|
sources: ["https://..."]
|
||||||
|
notes: "Encodeur Qwen3-VL, VAE Qwen Image. LoRA-friendly."
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ Règle anti-hallucination gravée dans le modèle de données : tout champ non vérifié publiquement prend la valeur `null` + affichage « ⚠️ à vérifier » — jamais de valeur inventée.
|
||||||
|
|
||||||
|
### 4.2 Profils projets (MVP)
|
||||||
|
CRUD sur `data/profils_projets.yaml` — pré-rempli avec les 5 contextes :
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- id: vn_renpy_adulte
|
||||||
|
nom: "VN Ren'Py (CC BY-NC / BY-NC-ND)"
|
||||||
|
requiert: [commercial_ok]
|
||||||
|
exclut: []
|
||||||
|
preferences:
|
||||||
|
nsfw_ok: obligatoire
|
||||||
|
poids_ouverts: fortement_prefere
|
||||||
|
comfyui_natif: prefere
|
||||||
|
plateformes: [steam, itch_io]
|
||||||
|
contraintes_specifiques: "Déclaration IA Steam + tag itch.io obligatoires. NSFW => local uniquement."
|
||||||
|
|
||||||
|
- id: prestation_images
|
||||||
|
nom: "Vente d'images (prestataire)"
|
||||||
|
requiert: [commercial_ok, licence_outputs_commercialisables]
|
||||||
|
exclut: [attribution_requise] # configurable selon client
|
||||||
|
...
|
||||||
|
|
||||||
|
- id: clips_vendus / jeu_steam_itch / diffusion_libre # idem
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 Moteur de recommandation (MVP)
|
||||||
|
- Entrée : `profil_id` + filtres ad hoc (`modalite`, `vram_max`, `budget: 0`).
|
||||||
|
- Logique : filtrage dur (requiert/exclut) → scoring pondéré (préférences, rang arena, fraîcheur `derniere_verif`, pénalité `statut != production` et champs `null`).
|
||||||
|
- Sortie : shortlist classée **avec justification par modèle** (pourquoi éligible, quels warnings : attribution, seuil, licence à vérifier, déclaration plateforme).
|
||||||
|
- Route API `POST /api/recommander` + page UI dédiée.
|
||||||
|
|
||||||
|
### 4.4 Inbox alertes (MVP)
|
||||||
|
- `POST /api/alerts` (token secret) reçoit les événements classifiés depuis Node-RED.
|
||||||
|
- Schéma : `{type: nouvelle_sortie|changement_licence|changement_classement|comfyui_support, modele_id?, titre, resume, impact_profils: [...], urgence, sources[], date}`.
|
||||||
|
- UI : inbox filtrable par profil impacté ; action « Créer/MAJ fiche modèle » qui pré-remplit le registre ; action « Marquer traité ».
|
||||||
|
|
||||||
|
### 4.5 Exporteur de documents (MVP)
|
||||||
|
Bouton « Régénérer » : produit les guides markdown (licences, classements) **depuis le registre** — les documents deviennent des exports, plus des fichiers maintenus à la main. Templates markdown éditables dans l'UI.
|
||||||
|
|
||||||
|
### 4.6 Templates ComfyUI (Phase 2)
|
||||||
|
- Stockage de workflows JSON paramétrés (variables : `{{modele_checkpoint}}`, `{{vram_profile}}`, `{{resolution}}`).
|
||||||
|
- Quand `registre` change → régénération des JSON concrets + commit git.
|
||||||
|
- Smoke test headless : POST du prompt canari sur l'API ComfyUI (`/prompt`) de la machine de prod, remontée du statut dans l'UI.
|
||||||
|
- ⚠️ Ne pas promettre la réécriture automatique de graphes : on paramètre, on ne régénère pas la topologie.
|
||||||
|
|
||||||
|
## 5. Flows Node-RED à livrer (JSON importables)
|
||||||
|
|
||||||
|
1. **`rss-ingest`** : poll flux (HF blog, BFL, MiniMax, Krea, Artificial Analysis, changelog ComfyUI, GitHub releases) via FreshRSS API ou nodes RSS → dédup → POST classifier.
|
||||||
|
2. **`license-watch`** : réception webhooks changedetection_ynh (URLs ToS/licences configurées : BFL, MiniMax Community License, cards HF, Midjourney, Runway, Suno, Steam/itch.io policies) → diff → POST classifier avec `type: changement_licence`, urgence haute.
|
||||||
|
3. **`classifier`** : HTTP POST vers Ollama (`/api/chat`, `format: json`) avec prompt système figé (cf. §6) → POST `/api/alerts`.
|
||||||
|
4. **`notify`** : routage urgence haute → email (SMTP YunoHost) et/ou ntfy/Matrix ; le reste reste en inbox.
|
||||||
|
|
||||||
|
## 6. Prompt système du classifieur (figé, versionné)
|
||||||
|
|
||||||
|
```
|
||||||
|
Tu es un classifieur d'événements IA pour un créateur indépendant.
|
||||||
|
Réponds UNIQUEMENT en JSON valide respectant ce schéma :
|
||||||
|
{type, modele, editeur, modalites[], licence: {nom, commercial_ok(nullable),
|
||||||
|
seuil(nullable), attribution_requise(nullable), poids_ouverts(nullable)},
|
||||||
|
nsfw_ok(nullable), vram_gb(nullable), comfyui_natif(nullable), statut,
|
||||||
|
impact_profils[], resume(2 phrases max), sources[], confiance: haute|moyenne|faible}
|
||||||
|
RÈGLES STRICTES : si une information n'est pas explicitement présente dans le
|
||||||
|
texte source, mets null. N'invente jamais une licence, un prix ou un benchmark.
|
||||||
|
Signale les benchmarks auto-rapportés par l'éditeur (confiance: moyenne max).
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Packaging YunoHost (`veil-le-ia_ynh`)
|
||||||
|
|
||||||
|
- Format packaging v2 (`manifest.toml`), helpers officiels, doc : doc.yunohost.org/packaging_apps.
|
||||||
|
- Ressources : `ports` (1 port node), `nodejs` (version LTS), `system_user`, `install_dir` (`/var/www/veille-ia`), `data_dir` (`/home/yunohost.app/veille-ia` : YAML + git).
|
||||||
|
- Service systemd via `ynh_add_systemd_config`, reverse proxy nginx via `ynh_add_nginx_config`.
|
||||||
|
- **SSO** : permission `visitors: false` — app privée derrière SSOwat ; header SSO validé côté SvelteKit (hooks) ou auth par défaut admin uniquement.
|
||||||
|
- Scripts : `install`, `remove`, `upgrade`, `backup`, `restore`, `change_url`.
|
||||||
|
- CI : `package_check` doit passer (niveau ≥ 7 visé).
|
||||||
|
- Licence du package et de l'app : AGPL-3.0 (cohérent libriste ; AGPL car app web).
|
||||||
|
|
||||||
|
## 8. Contraintes techniques & décisions
|
||||||
|
|
||||||
|
1. **Svelte** : recommandations détaillées fournies séparément par le porteur (`SPECS_SVELTE.md` à la racine du projet — le code doit les suivre strictement ; si absent, appliquer : SvelteKit + TypeScript strict + adapter-node, pas de framework CSS lourd sans accord).
|
||||||
|
2. **Persistance : fichiers YAML + git** (pas de base de données au MVP — lisible, sauvegardable, diffable). SQLite via Drizzle admis en Phase 2 si le volume d'alertes le justifie.
|
||||||
|
3. **Zéro appel API payant** : tout le LLM passe par Ollama local. OpenRouter optionnel derrière un flag de config, désactivé par défaut.
|
||||||
|
4. **Ollama** : si paquet YunoHost indisponible/non maintenu → installation Docker hors YNH ou sur la machine locale du porteur, joignable par HTTP (wireguard/lan). Documenter les deux chemins dans le README.
|
||||||
|
5. **Anti-hallucination systémique** : champs nullables + badge « à vérifier » ; aucune écriture dans `registre` sans `sources[]` non vide.
|
||||||
|
6. **i18n** : français d'abord, structure prête pour créole/anglais.
|
||||||
|
|
||||||
|
## 9. Roadmap
|
||||||
|
|
||||||
|
| Phase | Contenu | Critère de sortie |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | Socle app SvelteKit + packaging YNH minimal (install/remove/backup OK) | `package_check` vert, app accessible via SSO |
|
||||||
|
| 1 | Registre + profils + moteur de recommandation + UI | Shortlist correcte sur les 5 profils pré-remplis |
|
||||||
|
| 2 | Inbox alertes + flow Node-RED `classifier` + export markdown | Une alerte changedetection réelle arrive classifiée dans l'inbox |
|
||||||
|
| 3 | Templates ComfyUI + smoke tests | Régénération JSON + test canari vert |
|
||||||
|
| 4 | Durcissement : i18n, SQLite si besoin, niveau 8 package_check | — |
|
||||||
|
|
||||||
|
## 10. Hors périmètre (v1)
|
||||||
|
|
||||||
|
- Réécriture automatique de graphes ComfyUI (topologie) — paramétrage seulement.
|
||||||
|
- Scraping de plateformes à login (X/Twitter, Discord) — sources RSS/Atom/web uniquement.
|
||||||
|
- Multi-utilisateurs (app personnelle).
|
||||||
|
- Notifications push mobile natives (ntfy suffit).
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# PROMPT POUR KIMI CLI — Projet « veille-ia »
|
||||||
|
*(à coller tel quel dans Kimi CLI, depuis le dossier de travail contenant `plan_prd_veille_ia.md` ; ajouter `SPECS_SVELTE.md` à côté quand tes recommandations Svelte sont prêtes)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
RÔLE
|
||||||
|
Tu es un développeur full-stack senior spécialisé SvelteKit et packaging YunoHost.
|
||||||
|
Tu construis une application web appelée « veille-ia » : un observatoire personnel
|
||||||
|
de veille sur les modèles IA génératifs (sorties, licences, classements), avec
|
||||||
|
moteur de recommandation par profils de projets. Application monoprivée,
|
||||||
|
self-hosted, destinée à être packagée pour YunoHost et installée sur un serveur
|
||||||
|
personnel. Tout doit fonctionner sans aucune API payante.
|
||||||
|
|
||||||
|
DOCUMENTS DE RÉFÉRENCE (à lire INTÉGRALEMENT avant de coder)
|
||||||
|
1. ./plan_prd_veille_ia.md — PRD complet : architecture, schémas YAML, flows
|
||||||
|
Node-RED, règles métier. C'est la spécification fonctionnelle, suis-la.
|
||||||
|
2. ./SPECS_SVELTE.md — recommandations Svelte du porteur. Si le fichier existe,
|
||||||
|
il PRÉVAUT sur tout choix technique frontend. S'il est absent, applique :
|
||||||
|
SvelteKit + TypeScript strict + adapter-node, Svelte 5 (runes), aucun
|
||||||
|
framework CSS lourd (Tailwind autorisé), composants accessibles (clavier,
|
||||||
|
contrastes), aucune dépendance non maintenue.
|
||||||
|
3. Documentation officielle packaging YunoHost : https://doc.yunohost.org/packaging_apps
|
||||||
|
— VÉRIFIE le format actuel du manifest.toml (packaging v2) et des helpers
|
||||||
|
avant d'écrire le moindre script. Ne devine jamais une syntaxe de helper.
|
||||||
|
|
||||||
|
RÈGLES STRICTES
|
||||||
|
- Zéro hallucination : tout champ de donnée non vérifiable doit être `null`
|
||||||
|
et l'UI affiche un badge « ⚠️ à vérifier ». Aucune licence, aucun prix,
|
||||||
|
aucun benchmark ne peut être écrit en dur sans champ `sources[]` associé.
|
||||||
|
- Zéro dépendance propriétaire ou source-available (pas de n8n, pas de licence
|
||||||
|
non-OSI dans l'arbre de dépendances direct). Licence du projet : AGPL-3.0.
|
||||||
|
- Persistance : fichiers YAML dans le data_dir + commits git automatiques
|
||||||
|
(simple-git). Pas de base de données dans cette version.
|
||||||
|
- Tout appel LLM passe par un endpoint Ollama configurable (variable d'env
|
||||||
|
OLLAMA_URL, défaut http://127.0.0.1:11434). L'app doit démarrer et fonctionner
|
||||||
|
même si Ollama est injoignable (dégradation gracieuse, pas de crash).
|
||||||
|
- Sécurité : endpoint POST /api/alerts protégé par token (variable d'env
|
||||||
|
ALERTS_TOKEN générée à l'install). Aucune route mutative sans vérification.
|
||||||
|
- Commits git atomiques et explicites, en français. README.md à jour à chaque
|
||||||
|
phase.
|
||||||
|
|
||||||
|
LIVRABLES, DANS CET ORDRE (arrête-toi à la fin de chaque phase pour mon validation)
|
||||||
|
|
||||||
|
PHASE 0 — Socle + packaging
|
||||||
|
1. Scaffold SvelteKit (TypeScript strict, adapter-node) dans ./app/
|
||||||
|
2. Layout + navigation SSO-aware : l'app lit le header SSO YunoHost
|
||||||
|
(YNH_USER) via les hooks serveur ; sans header valide, page d'accueil
|
||||||
|
publique minimale uniquement.
|
||||||
|
3. Package YunoHost ./veille-ia_ynh/ : manifest.toml (packaging v2),
|
||||||
|
scripts install / remove / upgrade / backup / restore / change_url,
|
||||||
|
ressources ports + nodejs + system_user + install_dir + data_dir,
|
||||||
|
service systemd, config nginx. Respecte la doc officielle vérifiée.
|
||||||
|
4. Script de dev local : l'app doit aussi tourner hors YunoHost
|
||||||
|
(variables d'env documentées).
|
||||||
|
|
||||||
|
PHASE 1 — Cœur métier
|
||||||
|
5. data/registre_modeles.yaml + data/profils_projets.yaml créés au premier
|
||||||
|
démarrage, pré-remplis EXACTEMENT avec les schémas et les 5 profils du PRD
|
||||||
|
(vn_renpy_adulte, prestation_images, clips_vendus, jeu_steam_itch,
|
||||||
|
diffusion_libre). Les données d'exemple de modèles viennent du PRD §4.1.
|
||||||
|
6. CRUD registre + profils (pages + routes serveur), validation Zod des
|
||||||
|
schémas YAML, commit git à chaque écriture.
|
||||||
|
7. Moteur de recommandation POST /api/recommander + page UI :
|
||||||
|
filtrage dur (requiert/exclut) puis scoring pondéré (préférences,
|
||||||
|
rang arena, fraîchier derniere_verif, pénalités statut et champs null).
|
||||||
|
Chaque recommandation affiche sa justification et ses warnings
|
||||||
|
(attribution requise, seuil de revenus, licence à vérifier,
|
||||||
|
déclarations plateformes).
|
||||||
|
8. Exporteur markdown : génération des documents guide/classements depuis
|
||||||
|
le registre via templates éditables.
|
||||||
|
|
||||||
|
PHASE 2 — Alertes (ne commence qu'après ma validation de la phase 1)
|
||||||
|
9. POST /api/alerts (token) + schéma d'alerte du PRD + inbox UI filtrable
|
||||||
|
par profil impacté + actions « créer/MAJ fiche » et « traité ».
|
||||||
|
10. Flows Node-RED exportés en JSON importable dans ./nodered-flows/
|
||||||
|
(rss-ingest, license-watch, classifier, notify) conformes au PRD §5,
|
||||||
|
avec le prompt classifieur du PRD §6 embarqué tel quel.
|
||||||
|
|
||||||
|
QUALITÉ
|
||||||
|
- `npm run check` (svelte-check) et `npm run lint` propres à chaque commit.
|
||||||
|
- Tests : vitest sur le moteur de recommandation (cas nominaux + cas limites :
|
||||||
|
licence null, seuil dépassé, modèle statut "mort").
|
||||||
|
- À la fin de chaque phase, imprime : ce qui est fait, ce qui reste,
|
||||||
|
les commandes de test, et ATTENDS ma validation avant de continuer.
|
||||||
|
- Si une information te manque (version de helper YunoHost, comportement
|
||||||
|
d'API), cherche la doc officielle ; si introuvable, pose-moi la question
|
||||||
|
au lieu de supposer.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes d'usage (pour toi, pas pour Kimi CLI)
|
||||||
|
|
||||||
|
1. **Ordre de préparation** : écris d'abord ton `SPECS_SVELTE.md` (conventions, runes vs stores, Tailwind ou non, structure de dossiers, accessibilité, i18n) et dépose-le à côté du PRD — le prompt lui donne la priorité.
|
||||||
|
2. **Validation par phases** : le prompt impose un arrêt entre phases. Ne laisse pas Kimi CLI enchaîner 0→2 d'un coup : le packaging YunoHost (phase 0) est le point le plus à risque, vérifie `package_check` toi-même.
|
||||||
|
3. **Ollama** : si `ollama_ynh` n'est pas maintenu au moment de l'install, fais tourner Ollama sur ta machine locale et expose-le au serveur via VPN/wireguard — l'app y survivra (dégradation gracieuse prévue).
|
||||||
|
4. **Après installation** : configure dans changedetection_ynh les URLs de ToS/licences à surveiller (liste dans ton guide licences §9) avec notification webhook vers Node-RED.
|
||||||
|
5. **Évolution future** : quand le registre sera stable, on pourra y brancher mon digest hebdomadaire (cron) — je te fournis les diffs, l'app les ingère via `/api/alerts`.
|
||||||
Reference in New Issue
Block a user