veza/docs/FRONTEND_DEEP_DIVE_AUDIT.md

190 lines
16 KiB
Markdown
Raw Normal View History

# Audit Deep Dive — Frontend Veza (vs standards SaaS type Discord/Spotify)
**Date** : 2026-02-05
**Périmètre** : `apps/web` — structure des composants, styles (Tailwind / KŌDŌ), stories Storybook, décorateur global, accessibilité.
**Référentiel** : cohérence design system, états dinterface, complexité des composants, illusion applicative, a11y.
---
## Synthèse exécutive
Le frontend sappuie sur un **système de design KŌDŌ** riche (tokens CSS, thème clair/sombre) et un **Storybook stabilisé** (0 erreur réseau/console, contrat documenté). Les écarts principaux par rapport à des standards type Discord/Spotify portent sur : **utilisation encore forte de valeurs arbitraires** malgré les tokens, **états dinterface incomplets** dans de nombreuses stories (Loading/Error/Disabled/Focus), **plusieurs composants “monolithes”** difficiles à tester et à faire évoluer, **décorateur Storybook** sans gestion des transitions ni du feedback tactile, et **a11y** partiellement couverte (addon présent, tests a11y en “todo”).
Les problèmes sont classés en **Bloquant**, **Amélioration** et **Perfectionnement**.
---
## 1. Cohérence du système de design
### Ce qui est en place
- **Tokens centralisés** dans `src/index.css` : palette (void, cyan, magenta, lime), sémantiques (primary, destructive, success, warning), radius (`--radius`), durées (`--duration-fast`, `--duration-normal`, etc.), easings, glows, glass.
- **Thème Tailwind v4** via `@theme inline` : couleurs (`--color-primary`, `--color-sidebar-*`), radius (`--radius-sm` à `--radius-2xl`), typo (font-sans, font-mono, font-display).
- **Composants UI de base** : `button.tsx` utilise des tokens (`bg-kodo-cyan`, `ring-kodo-cyan`, `focus-visible:ring-2`), CVA pour variants/sizes, pas de valeurs arbitraires dans les variants.
### Problèmes identifiés
| Criticité | Constat | Détail |
|-----------|--------|--------|
| **Amélioration** | Valeurs arbitraires nombreuses | Plus de **170 fichiers** contiennent des classes type `h-[…]`, `w-[…]`, `p-[…]`, `gap-[…]`, `rounded-[…]`. Ex. : `h-[400px]`, `w-[300px]`, `min-h-[600px]`, `w-[500px]`, `h-[70px]` dans stories et composants. |
| **Amélioration** | Stories comme source de dérive | Les stories utilisent souvent des conteneurs en `h-[600px]`, `w-[300px]` pour le rendu. Ces valeurs ne sappuient pas sur les tokens (ex. `--radius-lg`, `h-96`, `max-w-3xl`). |
| **Perfectionnement** | Échelle despacement | Les tokens despacement (4px, 8px, 16px, 24px…) sont partiellement reflétés en Tailwind ; des `p-4`, `p-8` coexistent avec des `p-[12px]` ou `gap-[11px]` ponctuels. |
### Recommandations
- **Remplacer progressivement** les valeurs arbitraires par des tokens : par ex. `h-[400px]``h-[var(--height-panel)]` ou `min-h-96` si une scale est définie.
- **Documenter une scale de hauteurs/largeurs** (cards, modals, sidebars) dans le design system et lutiliser dans les stories.
- **Linter / règle custom** (stylelint ou ESLint) pour limiter les `[…px]` / `[…]rem` hors tokens.
### Mise à jour (nettoyage systémique)
**Layout primitives** ont été ajoutées dans `src/index.css` pour **App/Layouts** et **App/Pages** :
- **Variables CSS** (`:root`) : `--layout-content-max-width` (100rem), `--layout-main-min-height`, `--layout-page-min-height` (37.5rem), `--layout-page-min-height-sm` (25rem), `--layout-story-decorator-min-height` (12rem).
- **Classes utilitaires** (`@layer utilities`) : `.max-w-layout-content`, `.min-h-layout-main`, `.min-h-layout-page`, `.min-h-layout-page-sm`, `.min-h-layout-story`.
- **Remplacements effectués** : `max-w-[1600px]``max-w-layout-content` (Layout, DashboardLayout, ChatPage) ; `min-h-[calc(100vh-64px)]``min-h-layout-main` (Layout) ; `min-h-[600px]``min-h-layout-page` (UserProfilePage, SettingsPage, PlaylistDetailPage) ; `min-h-[400px]``min-h-layout-page-sm` (LibraryPage, SessionsPage) ; `min-h-[200px]` (Navbar story) → `min-h-layout-story` ; `md:w-[400px]` (AudioPlayer) → `md:w-96` ; `min-h-[44px]``min-h-11` (PlaylistListPage) ; `min-w-[140px]``min-w-36` (SettingsPage) ; `h-[60%]` / `h-[40%]``h-3/5` / `h-2/5` (LibraryPage). Les ombres et valeurs viewport (ex. `h-[60vh]`) sont laissées en létat.
---
## 2. Qualité des états dinterface (stories)
### Ce qui est en place
- **Loading / Skeleton** : `LoadingState`, `Skeleton`, `TrackListSkeleton`, `PlayerLoading` ont des stories dédiées.
- **Error** : `ErrorDisplay`, `PlayerError`, `AuthErrorMessage`, `ErrorBoundary`, `Alert` (variant error) sont couverts.
- **Focus** : Le `Button` a `focus-visible:ring-2 focus-visible:ring-kodo-cyan` ; plusieurs composants UI (input, checkbox, select, tabs) ont du `focus:` ou `focus-visible:`.
- **Disabled** : Présent dans ~23 fichiers de stories (Button, Checkbox, VolumeControl, etc.).
### Problèmes identifiés
| Criticité | Constat | Détail |
|-----------|--------|--------|
| **Bloquant** | Addon a11y non exploité | Dans `.storybook/preview.tsx`, `a11y: { test: 'todo' }` — les tests a11y ne sont pas exécutés en CI ni utilisés comme garde-fou. |
| **Amélioration** | Loading “factice” sur Button | La story `LoadingState` du Button est un `disabled` + texte "Loading..." sans spinner ni `aria-busy`. Les standards SaaS montrent un état de chargement explicite (spinner + désactivation). |
| **Amélioration** | États manquants par type de composant | Beaucoup de composants “carte” ou “liste” nont quun état Default : pas de story **Empty**, **Error**, **Loading** ou **Disabled** (ex. plusieurs vues Playlist, Track, Chat, Commerce). |
| **Amélioration** | Hover / Active peu documentés | Peu de stories nommées "Hover" ou "Active" ; les variants sont surtout visuels. Pas de démo systématique des états interactifs (hover/active) pour les boutons et liens. |
| **Perfectionnement** | Focus visible inégal | Une soixantaine de fichiers de composants utilisent `focus:` ou `focus-visible:` ; dautres (cards cliquables, list items, custom controls) peuvent ne pas avoir de ring/outline visible. |
### Recommandations
- Passer **a11y.test** de `'todo'` à une config réelle (ex. `runOnly` avec règles WCAG 2.1 AA) et faire échouer le build/audit si des violations sont détectées.
- Ajouter une story **Loading** réaliste au Button (spinner + `disabled` + `aria-busy`) et un état **Disabled** explicite partout où laction peut être désactivée.
- Pour les listes/cartes (Playlist, Track, Chat, Commerce), ajouter au moins **Empty** et **Error** (et **Loading** si async).
- Documenter **Hover** / **Active** dans les stories des composants interactifs (boutons, nav, sidebar) pour valider la cohérence visuelle.
---
## 3. Complexité des composants (“composants dieux”)
### Composants les plus volumineux (≥ ~400 lignes)
| Fichier | Lignes | Responsabilités identifiées |
|---------|--------|------------------------------|
| `ProfileForm.tsx` | 678 | Formulaire profil, validation, champs multiples, sous-vues, édition avatar, etc. |
| `ProfileView.tsx` | 602 | Layout profil, onglets, stats, contenu dynamique, modales. |
| `GearView.tsx` | 559 | Vue “équipement”, liste, filtres, actions, détail. |
| `CommentThread.tsx` | 547 | Arbre de commentaires, réponses, édition, suppression, chargement paginé. |
| `LazyComponent.tsx` | 505 | Lazy load, fallback, erreur, timeout, plusieurs modes. |
| `CloudFileBrowser.tsx` | 503 | Vue liste/grid, recherche, tri, filtres par tags, sélection, modales (metadata, watermark), détail fichier, navigation. |
| `Search.tsx` | 494 | Recherche globale, suggestions, historique, catégories, résultats. |
| `UploadModal.tsx` | 486 | Upload, drag & drop, progression, métadonnées, validation. |
| `file-upload.tsx` | 478 | Gestion fichiers, preview, validation, multi-type. |
| `ChatSidebar.tsx` | 469 | Liste salons, création, recherche, états (non lu, favoris). |
| `select.tsx` | 466 | Select avec recherche, multi, combobox, keyboard nav. |
### Problèmes identifiés
| Criticité | Constat | Détail |
|-----------|--------|--------|
| **Amélioration** | Trop de responsabilités dans un seul fichier | **CloudFileBrowser** : vue (list/grid), recherche, tri, filtres, tags, sélection, 2 modales, détail fichier, appels service. Difficile à tester unitairement et à faire évoluer (ex. ajouter un état “empty” ou “error” propre). |
| **Amélioration** | Logique métier + UI mélangées | **CommentThread**, **ProfileForm**, **Search** : logique (appels API, état) et rendu (JSX) dans le même composant ; pas de découpage net en hooks + sous-composants. |
| **Perfectionnement** | Réutilisabilité limitée | **GearView**, **ProfileView** : vues “pages” très spécifiques ; extraire des blocs (Header, Stats, List, Filters) permettrait des stories et des tests plus ciblés. |
### Recommandations
- **Découper CloudFileBrowser** : extraire `FileBrowserToolbar`, `FileBrowserGrid`, `FileBrowserList`, `FileTagFilter`, et un hook `useCloudFiles` (loading, error, data). Chaque bloc avec sa story (Default, Loading, Empty, Error).
- **Extraire la logique** de CommentThread (hooks `useComments`, `useCommentActions`) et garder un composant de rendu (thread + réponses) plus simple.
- **ProfileForm / ProfileView** : séparer par section (Avatar, Infos, Sécurité, etc.) en composants ou sous-vues avec leurs propres stories.
- Appliquer le même principe (un rôle par composant, données via props ou hooks) pour **Search**, **ChatSidebar**, **UploadModal**, **select** afin daméliorer la testabilité et la maintenabilité.
---
## 4. Fidélité de lillusion applicative (StorybookDecorator)
### Ce qui est en place
- **Providers centralisés** : `I18nextProvider`, `ThemeProvider`, `QueryClientProvider`, `ToastProvider`, `AudioProvider`, `AuthProvider`, `MemoryRouter`.
- **Router** : `parameters.router.initialEntries` pour les stories dépendant dune route.
- **Thème** : bascule light/dark via globals Storybook (backgrounds).
- **Toasts** : en mode Storybook, toasts loggés en console (pas daffichage intrusif).
### Problèmes identifiés
| Criticité | Constat | Détail |
|-----------|--------|--------|
| **Amélioration** | Pas de gestion des transitions | Aucun wrapper `prefers-reduced-motion` ni fourniture de context pour désactiver les animations en Storybook. Les transitions CSS (`--duration-*`) sont appliquées telles quelles ; pas de mode “réduit” pour les tests ou laccessibilité. |
| **Amélioration** | Feedback tactile non simulé | Pas de décorateur ou paramètre pour simuler états tactiles (active, press) ou pointer (hover) de façon déterministe. Utile pour valider les états “pressed” / “hover” de boutons et cartes. |
| **Perfectionnement** | Couleurs de fond en dur | Le décorateur utilise `#0a0a0a` et `#ffffff` en inline style au lieu des tokens (`var(--background)`). En cas dévolution du thème, le conteneur Storybook peut diverger de lapp. |
| **Perfectionnement** | Pas de “viewport” par défaut | Des viewports custom (mobile, tablet, desktop) existent ; le décorateur ne force pas de viewport par story. Pour un rendu type “app”, un conteneur largeur max (ex. 1440px) pourrait être optionnel. |
### Recommandations
- Ajouter un **paramètre** (ex. `parameters.motion: 'reduce'`) et un wrapper qui applique `prefers-reduced-motion: reduce` (media query ou class) pour tester le comportement sans animations.
- Utiliser **les tokens** pour le fond du conteneur : `background: 'var(--background)'` (et sassurer que les variables sont bien appliquées dans liframe).
- Optionnel : décorateur ou paramètre pour forcer un **état “interaction”** (hover/active) sur le premier élément focusable, pour valider le focus visible et les états actifs dans les stories.
---
## 5. Accessibilité (a11y)
### Ce qui est en place
- **ARIA / rôles** : utilisés dans une soixantaine de composants (table, select, modal, tabs, pagination, breadcrumbs, tooltip, checkbox, progress, alert, etc.).
- **Contrastes** : palette KŌDŌ en oklch avec séparation sémantique (primary, destructive, muted, etc.) ; fonds et textes prévus pour être lisibles.
- **Addon a11y** : présent dans Storybook ; config actuelle `a11y: { test: 'todo' }`.
- **Focus visible** : `button`, `input`, `checkbox`, `select`, `tabs` ont des styles `focus-visible:ring` ou équivalent.
### Problèmes identifiés
| Criticité | Constat | Détail |
|-----------|--------|--------|
| **Bloquant** | Tests a11y non actifs | Tant que `test: 'todo'`, aucune violation a11y ne fait échouer laudit ni le build. Risque de régressions (contraste, labels, rôles). |
| **Amélioration** | Contraste à vérifier sur cas réels | Les combinaisons `muted-foreground` sur `muted`, ou textes sur “glass” / dégradés, nont pas été vérifiées automatiquement. Un audit manuel ou des tests a11y ciblés (contrast, label) sont recommandés. |
| **Amélioration** | Zones cliquables non boutons | Cartes ou lignes entières cliquables sans `role="button"` ni `tabIndex={0}` ni gestion clavier (Enter/Space) — à vérifier dans les composants “card” et “list”. |
| **Perfectionnement** | Annonces live (toasts, chargement) | Pas de revue systématique des `aria-live`, `aria-busy`, `aria-atomic` pour les toasts et états de chargement. |
### Recommandations
- **Activer les tests a11y** dans Storybook : définir `a11y: { runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa'] } }` (ou équivalent) et intégrer le résultat à laudit (ou au moins au build Storybook) pour faire échouer en cas de violations.
- Vérifier les **cartes et listes cliquables** : soit `<button>` ou `div` avec `role="button"`, `tabIndex={0}`, et gestion `onKeyDown` (Enter/Space).
- Planifier un **audit contraste** (manuel ou outil) sur les écrans clés (login, player, sidebar, modales) et documenter les ratios pour les combinaisons utilisées.
---
## 6. Tableau de synthèse par criticité
| Criticité | Thème | Action prioritaire |
|-----------|--------|--------------------|
| **Bloquant** | a11y | Activer les tests a11y dans Storybook (retirer `test: 'todo'`, définir des règles WCAG et les faire échouer le job si besoin). |
| **Amélioration** | Design system | Réduire les valeurs arbitraires (h-[…], w-[…]) en les remplaçant par des tokens ou des utilitaires Tailwind cohérents. |
| **Amélioration** | États dinterface | Compléter les stories : Loading réaliste (Button), Empty/Error/Loading pour listes et cartes, états Disabled et si possible Hover/Active. |
| **Amélioration** | Composants dieux | Découper CloudFileBrowser, CommentThread, ProfileForm/ProfileView en sous-composants et hooks testables. |
| **Amélioration** | Illusion applicative | Utiliser les tokens pour le fond du décorateur ; ajouter support reduced-motion et optionnellement états hover/active. |
| **Amélioration** | a11y | Vérifier cartes/listes cliquables (rôle, clavier) ; audit contraste sur écrans clés. |
| **Perfectionnement** | Design system | Scale despacement/hauteurs documentée ; linter pour limiter les valeurs arbitraires. |
| **Perfectionnement** | États dinterface | Stories Hover/Active explicites ; revue focus visible sur tous les contrôles. |
| **Perfectionnement** | Composants | Extraire blocs réutilisables des vues “page” (GearView, ProfileView, Search). |
| **Perfectionnement** | a11y | Revue `aria-live` / `aria-busy` pour toasts et chargement. |
---
## 7. Prochaines étapes suggérées
1. **Court terme** : Activer a11y dans Storybook et corriger les violations bloquantes ; ajouter les stories Empty/Error (et Loading si pertinent) aux composants listes/cartes les plus utilisés.
2. **Moyen terme** : Découper 23 composants “dieux” (ex. CloudFileBrowser, CommentThread) et aligner les nouvelles stories sur les tokens (réduction des valeurs arbitraires).
3. **Long terme** : Documenter une grille de hauteurs/largeurs et une checklist “états dinterface” pour toute nouvelle story ; intégrer reduced-motion et feedback tactile dans le décorateur.
---
*Rapport généré dans le cadre de laudit frontend Veza — Storybook stabilisé, contrat dans `docs/STORYBOOK_CONTRACT.md`.*