Files
veille-ia-gen/SPECS_SVELTE.md
T
cyber-mawonaj dc6bf1bbde 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).
2026-08-01 09:18:27 -04:00

14 KiB
Raw Blame History

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) :

: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).