Inclut SPECS_SVELTE.md : conventions Svelte 5/SvelteKit pour veille-ia, consolidées depuis doc2sveltekit-transition (playbook, charte OKI, best practices).
14 KiB
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 dedoc2sveltekit-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
nodejsYunoHost 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 leDATA_DIRqu'au premier démarrage (jamais d'écrasement de données existantes).
3. Conventions Svelte 5 (obligatoires)
- Runes only.
$stateuniquement pour du rĂ©actif ; gros objets rĂ©assignĂ©s (rĂ©ponses API) â$state.raw. $derivedpour tout calcul ;$derived.bysi l'expression est complexe. Jamais$effectpour 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={...}(jamaison: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 declass:. - Liens internes via
resolve()de$app/paths; assets viaasset(). 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.tsavec$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.tsou 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) ;
fetchJSON uniquement pour les endpoints API externes (/api/*). - Persistance de préférences :
?? défaut, jamais|| défaut(un0/falsepersisté 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 estnull. 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-themeposĂ©e parstatic/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-faceavecfont-display: swap, preload de la display uniquement. Aucun appel Google Fonts. - Motion : micro-interactions sobres uniquement (
--dur-tanbousur hover/focus,--dur-mesuresur transitions de vues). Uniquementtransform+opacity. DurĂ©es lues depuis les tokens, jamais hardcodĂ©es. Gate uniqueprefers-reduced-motiondansbase.css(contenu statique complet). - Styles scopĂ©s dans les composants ; personnalisation parentâenfant via custom properties ;
:globalen dernier recours. - Layout « app » :
height: 100dvh+ overflow par panneau si pertinent ; cibles tactiles â„ 44 px. - Listes longues (registre, inbox) :
content-visibility: auto+contain-intrinsic-sized'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é viaaria-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.tslit le headerYnh-UserinjectĂ© par SSOwat viaproxy_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,/alerteset les form actions exigent un utilisateur. En dev local,DEV_USERsimule le header (documentĂ©, ignorĂ© en production). - CSP via la config SvelteKit (
kit.csp, modenonce, dansvite.config.tsâ SvelteKit â„ 2.62 n'utilise plussvelte.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.jsest un fichier externe. - Sous-chemin : l'app doit fonctionner servie sous
https://domaine.tld/veille:paths.relative: falsedanssvelte.config.js, base injectée par variable d'env au build/runtime, et tous les liens viaresolve()/asset(). POST /api/alerts: tokenALERTS_TOKEN(headerAuthorization: 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 :
consolestructuré (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 : licencenull, seuil de revenus dĂ©passĂ©, modĂšle statutmort, champnullpĂ©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-motionsur 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).