veza/apps/web/AUDIT_STRATEGIQUE_FRONTEND_2025.md
senke 39b2b642d2 feat(web): UI premium Discord/Spotify-like — tokens, shadows, focus, layout
Plan UI premium 6–8 semaines (design system, shell, Storybook, a11y):

- Design system: DESIGN_TOKENS.md, APP_SHELL.md, FULL_LAYOUT_PAGE.md. Single source
  for layout/shell (index.css), shadows (design-system.css), durations/easing.
- Tokens: shadow-cover-depth, shadow-gold-glow, shadow-fab-glow; layout max-height
  (max-h-layout-drawer, max-h-layout-panel, max-h-layout-list). All duration-200/300/500
  replaced by --duration-fast/normal/slow. Arbitrary shadows replaced by token classes.
- Shell & player: Sidebar, Header, GlobalPlayer, MiniPlayer, PlayerQueue, PlayerControls,
  AudioPlayer use tokens; focus-visible on Sidebar, PlayerQueue, DropdownMenuTrigger/Item,
  TabsTrigger. Typography: text-[10px]/[9px] → text-xs where applicable.
- ESLint: no-restricted-syntax (warn) for w-/h-/rounded-/shadow-/text-/spacing arbitrary.
- Scripts: report-arbitrary-values.mjs, capture/compare/generate visual; visual-complete.spec.ts.
- Stories full layout: Dashboard, Playlists, Library, Settings, Profile in DashboardLayout.stories.
- .cursorrules + README: DESIGN_TOKENS, APP_SHELL, visual commands, no arbitrary without justification.
- apps/web/.gitignore: e2e test artifacts (test-results-visual, playwright-report-visual).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 17:15:58 +01:00

280 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Audit stratégique frontend — Veza
**Date** : 2025-02-08
**Équipe fictive** : Staff Frontend Engineer, Principal Product Designer, Architecte logiciel senior, Tech Lead perf/a11y/DX, Product Manager.
**Objectif** : Diagnostic sans complaisance, écart vs Discord/Spotify, feuille de route et recommandations actionnables.
---
## A. Diagnostic brutal mais honnête
### Niveau global de lapplication
**Verdict : intermédiaire à senior (6/10), avec des poches de niveau amateur et quelques bases solides.**
Justification en une phrase : *Vous avez mis en place une structure feature-based, un design system documenté, des patterns (skeletons, optimistic UI, MSW), et une batterie de tests/stories, mais lexécution est inégale, les règles du projet sont régulièrement violées (valeurs arbitraires, double source dauth), et le ressenti “produit fini” est entamé par des correctifs visibles (fix-*.css, patches dans main.tsx) et une cohérence a11y/UX partielle.*
- **Architecture** : Correcte en intention (features/, services/, stores/, router lazy), mais **artisanale** dans les détails (double source dauth, callbacks no-op en route config, stores globaux vs feature stores).
- **Qualité du code** : Bonne sur les parties auditées (auth, playlists, player), **dette structurelle** (duplication auth Context vs Store, ~130 fichiers avec valeurs Tailwind arbitraires malgré une règle explicite).
- **Performance** : Bonne base (lazy routes, React Query, skeletons), mais pas de stratégie Suspense/streaming ni de priorisation explicite perçue vs mesurée. Voir *Checklist priorisation performance* ci-dessous.
- **Design system** : **Solide** sur le papier (KŌDŌ v3, tokens, layout primitives, dark mode), **fragile** en pratique (règles de layout violées massivement, pas de lint pour les primitives).
- **UX / micro-interactions** : Suffisante pour un produit interne, **insuffisante** pour un produit “premium” (feedback incohérent, états vides/erreur pas toujours traités de façon homogène).
- **Accessibilité** : Présente (aria, roles sur une partie des composants), mais **bonus** et non pilier (a11y Storybook en `todo`, pas de politique WCAG claire).
- **DX** : **Correcte** (docs internes, MSW, Storybook centralisé), mais **terrain miné** sur certains sujets (deux façons daccéder à lauth, nombreux fix-* à comprendre, seuils de coverage peutêtre non tenus).
---
### 5 forces réelles
1. **Design system pensé et documenté**
`index.css` : palette sémantique, layout primitives (`max-w-layout-content`, `min-h-layout-main`, etc.), easings, keyframes, dark mode. Les règles du projet interdisent les valeurs arbitraires et imposent ces primitives — même si non respectées partout, la cible est claire.
2. **Storybook-first et MSW bien intégrés**
Preview avec MSW en mode strict (`onUnhandledRequest: 'error'`), décorateur global (Theme, QueryClient, Router, Toast, Audio, Auth, i18n), pas dimport de providers dans les stories. Handlers MSW très complets (~1600 lignes). Environ 324 stories : bonne couverture de surface.
3. **Patterns de chargement et derreur explicites**
`LOADING_STATES_PATTERN.md`, composant `LoadingState` (spinner / inline / skeleton / minimal), skeletons dédiés par page (UserProfilePage, CloudFileBrowser, CourseDetailView, etc.). Données servies par React Query avec états loading/error gérés au niveau page.
4. **Optimistic UI et utilitaires métier**
`OPTIMISTIC_UPDATES.md` et `optimisticUpdates.ts` (createOptimisticUpdate, createArrayOptimisticUpdate, createToggleOptimisticUpdate). Utilisation dans playlists, likes, commentaires. Rollback et invalidation documentés.
5. **Surface de tests unitaires et dintégration**
~186 fichiers `*.test.tsx` et ~100 `*.test.ts`, intégration auth/playlists/player, Vitest avec coverage (seuils 80 % configurés). E2E Playwright et visuels présents. Base saine pour monter en rigueur.
---
### 5 faiblesses structurelles majeures
1. **Double source de vérité pour lauth**
`AuthContext` + `useAuth()` (Storybook, ProtectedRoute, quelques pages) vs `authStore` (Zustand) + `useAuthStore()` (App, Sidebar, Login, etc.). `App.tsx` et hydratation utilisent le store ; le décorateur Storybook utilise le Context. Risque de désync, double appel à lAPI, et confusion pour tout nouveau dev. Dette darchitecture prioritaire.
2. **Règles de layout systématiquement violées**
La règle “pas de valeurs arbitraires (w-[…], z-[…], etc.)” et “utiliser les layout primitives” est explicite dans `.cursorrules` et `index.css`, mais environ **130 fichiers** utilisent `w-[`, `h-[`, `z-[`, etc. La Sidebar ellemême utilise `w-64`, `z-[90]`, `z-[95]`, `left-6`, `top-20`. Le design system est sousexploité et la cohérence visuelle fragilisée.
3. **Correctifs applicatifs visibles et fragiles**
`main.tsx` et `App.tsx` chargent plusieurs correctifs : `fix-input-focus.css`, `fix-login-form.css`, `fixDisplayIssues`, `fixInputFocus`. Cela indique des problèmes de conception (focus, formulaires) résolus par patch plutôt quà la source. Coût de maintenance et risque de régression élevé.
4. **Route config et couplage produit**
`routeConfig.tsx` injecte des callbacks vides dans les éléments de route : `onCreateProduct={() => {}}`, `onNavigateTrack={() => {}}`. La routing ne doit pas porter de callbacks métier ; ces props devraient remonter dun layout ou dun contexte. Signe que la frontière entre routing et feature nest pas claire.
5. **Accessibilité et qualité perçue en option**
Storybook : `a11y: { test: 'todo' }`. Beaucoup de composants ont des `aria-*` ou `role`, mais de façon non systématique (ex. `KodoEmptyState` sans role/aria pour létat vide, Sidebar sans `role="navigation"` ni `aria-label`). Pas de politique WCAG ni de critères de sortie a11y. Laccessibilité reste un “plus” et non un pilier, ce qui limite la maturité produit et linclusivité.
---
## B. Écart avec Discord & Spotify
Tableau comparatif **technique et concret** (pas didéalisation).
| Dimension | Discord (référence) | Spotify (référence) | Veza (état actuel) | Écart principal |
|----------|--------------------|--------------------|--------------------|------------------|
| **Architecture** | Domains clairs, state par feature, peu de global ; routing découplé du métier. | App shell léger, données par view, cache agressif. | Features + stores globaux + double auth (Context + Store). | Une seule source dauth ; pas de callbacks dans la route config ; frontière nette layout/feature. |
| **UX** | Feedback immédiat, états loading/empty/error partout, modales et transitions prévisibles. | Transitions fluides, skeletons systématiques, pas de “trous” visuels. | Skeletons et LoadingState présents mais pas partout ; empty/error variables ; correctifs CSS. | Checklist loading/empty/error par écran ; suppression des fix-* par refonte des composants concernés. |
| **Performance** | Time to interactive court, lazy lourd, prioritisation du above-the-fold. | Streaming / progressive, cache et préchargement. | Lazy routes OK ; pas de Suspense boundary au niveau route ; pas de stratégie “perçu vs mesuré”. | Suspense + skeletons aux limites de route ; métriques perçues (LCP, INP) et objectifs chiffrés. |
| **Design** | Cohérence stricte, tokens partout, pas de valeurs en dur. | Design system unique, thème et spacing prévisibles. | Tokens et primitives définis mais ~130 fichiers en arbitraire ; primitives peu utilisées. | Lint (ou rule) qui interdit les arbitraires ; migration progressive vers primitives. |
| **Sensation de qualité** | App “qui ne bugue pas”, erreurs gracieuses, pas de demi-mesures visibles. | Polish, micro-interactions, pas de formulaires “cassés” en prod. | Correctifs visibles (fix-*), callbacks no-op, incohérences a11y. | Réduire la dette visible : un correctif = une issue de refonte ; zero no-op dans la config. |
En résumé : **Veza a les briques (design system, patterns, tests, Storybook)** mais manque de **rigueur dexécution** (une seule auth, respect des règles de layout, accessibilité et UX traitées comme non négociables) et de **finition** (plus de polish, moins de patches) pour se rapprocher de Discord/Spotify.
---
### Checklist priorisation performance
À traiter dans cet ordre pour maximiser limpact ressenti sans tout refactorer :
1. **First meaningful paint**
- Éviter tout JS bloquant avant le premier rendu (splitting par route déjà en place).
- Sassurer que le shell (header + sidebar ou placeholder) + un skeleton de contenu saffichent sans attendre les données.
2. **Skeletons systématiques**
- Chaque page qui fetch des données doit afficher un skeleton dédié (pas un spinner générique) pendant le chargement.
- Déjà partiellement fait ; compléter les écrans manquants (voir Phase 2).
3. **Optimistic UI pour les actions fréquentes**
- Like, follow, add to playlist, etc. : mise à jour immédiate + rollback si erreur.
- Déjà documenté et utilisé ; étendre aux actions qui nont pas encore le pattern.
4. **Réduction des re-renders inutiles**
- Vérifier les composants lourds (listes, tableaux) : memo, sélecteurs Zustand fins, ou découpage pour limiter les sous-arbre qui se re-rendent.
- React Query évite déjà des refetch inutiles si `staleTime` est configuré ; auditer les queryKeys et invalidation pour éviter des cascades.
5. **Métriques et objectifs**
- Mesurer LCP, INP (ou FID), TTI sur les parcours critiques (login, dashboard, lecture).
- Fixer des objectifs (ex. LCP &lt; 2.5 s sur 3G) et un process de revue en cas de régression.
6. **Suspense aux limites de route**
- Envelopper les arbres de route dans `<Suspense fallback={<PageSkeleton />}>` pour que le navigateur puisse afficher le fallback sans attendre tout le bundle enfant.
- Optionnel mais utile dès que le bundle par route grossit.
---
## C. Feuille de route de transformation
### Phase 1 — Fondations (ce qui doit être corrigé avant tout)
**Objectifs**
- Une seule source de vérité pour lauth.
- Arrêt des correctifs “magiques” et renforcement du respect du design system.
**Actions concrètes**
1. **Unifier lauth**
- Décider : soit tout sur Zustand (authStore), soit tout sur Context (AuthProvider). Recommandation : **Zustand** (déjà utilisé par App, hydratation, Sidebar).
- Migrer tous les usages de `useAuth()` vers `useAuthStore()` (ou linverse si vous choisissez Context).
- Faire du AuthProvider un simple consommateur du store (sil est gardé pour Storybook/compat).
- Supprimer la duplication et documenter la décision dans un ADR.
2. **Supprimer les callbacks de la route config**
- Retirer `onCreateProduct`, `onNavigateTrack`, etc. des éléments de route.
- Fournir ces comportements via layout (contexte ou props depuis un parent commun) ou navigation programmatique + state.
3. **Remplacer les fix-* par des corrections à la source**
- Pour chaque `fix-input-focus`, `fix-login-form`, etc. : identifier le composant (formulaire, input, focus trap) et corriger le focus, les états et le style dans le composant ou le design system.
- Supprimer les imports de fix-* une fois le comportement correct par défaut.
4. **Lint / règle pour les valeurs arbitraires**
- Règle ESLint (ou plugin Tailwind) interdisant les classes du type `w-[...]`, `h-[...]`, `z-[...]` pour layout/spacing (avec liste dexceptions si besoin).
- Introduire les exceptions progressivement et traiter les ~130 fichiers par batch (par répertoire ou par feature).
**Risques**
- Régression auth si la migration nest pas testée (E2E login/logout, refresh, Storybook).
- Régressions visuelles si on supprime les fix-* sans vérifier tous les écrans.
**Indicateurs de réussite**
- Un seul mécanisme dauth utilisé partout ; plus dimport de fix-* dans `main.tsx`/`App.tsx`.
- Aucun callback métier dans `routeConfig.tsx`.
- Lint en place et au moins un premier batch (ex. `components/layout`) sans violations.
---
### Phase 2 — Stabilisation et cohérence
**Objectifs**
- Loading / empty / error systématiques.
- Accessibilité traitée comme exigence, pas comme option.
- Design system respecté (primitives, tokens).
**Actions concrètes**
1. **Checklist par écran**
- Pour chaque page/feature : état Loading (skeleton dédié ou générique), Empty (KodoEmptyState ou équivalent), Error (ErrorDisplay + retry).
- Documenter la checklist et lappliquer aux nouvelles features ; remédier les écrans existants sans état manquant.
2. **Accessibilité**
- Remplacer `a11y: { test: 'todo' }` par une config réelle dans Storybook (règles axe-core, seuils).
- Définir un niveau cible (ex. WCAG 2.1 AA) et une liste de composants critiques (formulaires, navigation, player).
- Corriger Sidebar (role="navigation", aria-label), KodoEmptyState (role/aria pour empty state), et autres composants identifiés par un audit axe.
3. **Migration vers layout primitives**
- Remplacer progressivement les valeurs arbitraires par les classes du design system (`max-w-layout-content`, `min-h-layout-main`, etc.) et léchelle Tailwind.
- Prioriser : layout (Sidebar, Navbar, content), puis composants partagés (modales, cartes).
4. **Optimistic UI et erreurs**
- Vérifier que tous les usages doptimistic updates ont un rollback et un feedback utilisateur en cas déchec (toast, réversion visuelle).
- Centraliser la logique derreur (ex. `apiToastHelper`, ErrorDisplay) pour éviter des traitements ad hoc.
**Risques**
- Scope trop large si on veut tout migrer dun coup ; prioriser par trafic ou par criticité.
**Indicateurs de réussite**
- Chaque écran couvert par la checklist loading/empty/error.
- Storybook a11y activé et 0 violation sur les stories des composants critiques.
- Réduction mesurée du nombre de violations “arbitraires” (objectif chiffré par sprint).
---
### Phase 3 — Polish et excellence perçue
**Objectifs**
- Transitions et micro-interactions cohérentes.
- Performance perçue (skeleton, priorisation) et mesurée (LCP, INP).
- Réduction des “petits bugs” visuels et comportementaux.
**Actions concrètes**
1. **Suspense et streaming**
- Définir des Suspense boundaries au niveau des routes (ou des sous-arbres) avec fallback skeleton.
- Vérifier que le premier paint utile (header + skeleton contenu) est rapide.
2. **Métriques et objectifs**
- Suivre LCP, INP (ou FID), TTI.
- Définir des objectifs (ex. LCP < 2.5 s sur 3G) et un process de revue si dégradation.
3. **Micro-interactions**
- Utiliser les variables déjà définies (`--duration-fast`, `--ease-out`, etc.) partout cest pertinent (hover, focus, modales).
- Passer en revue les CTA et les listes interactives pour un feedback immédiat (disabled + spinner ou état loading sur le bouton).
4. **Nettoyage produit**
- Supprimer ou documenter tout code mort et composants non utilisés.
- Sassurer quaucun no-op ou placeholder visible ne reste dans les parcours principaux.
**Risques**
- Sur-optimisation sans mesure ; garder les métriques comme garde-fou.
**Indicateurs de réussite**
- LCP/INP dans la cible sur les parcours critiques.
- Pas de régression a11y ; design system respecté sur les nouvelles livraisons.
---
### Phase 4 — Scalabilité et long terme
**Objectifs**
- Code prêt pour 10× fonctionnalités et 10× équipe.
- Dette technique visible et pilotée.
- Expérimentations (A/B, feature flags) sans tout casser.
**Actions concrètes**
1. **Frontières de domaines**
- Clarifier ce qui vit dans `features/` vs `components/` (ex. pas de duplication forms auth dans les deux).
- Règle simple : composants réutilisables et design system dans `components/` ; logique métier et pages dans `features/`.
2. **State et données**
- Documenter quand utiliser React Query vs Zustand (server state vs client state).
- Éviter de dupliquer des données serveur dans des stores globaux (library, etc.) sans stratégie dinvalidation claire.
3. **Feature flags et expérimentations**
- Introduire un mécanisme léger de feature flags (build-time ou runtime) pour découpler déploiement et activation.
- Tester une feature derrière flag sans impacter les parcours principaux.
4. **Documentation et onboarding**
- Un README frontend à jour (structure, auth unique, design system, comment lancer Storybook et tests).
- Créer ou mettre à jour `docs/STORYBOOK_CONTRACT.md` (référencé dans le code) : états requis (Loading, Error, Empty), pas de providers dans les stories, MSW pour lAPI.
**Risques**
- Refactors trop larges ; privilégier des incréments et des ADR pour les décisions darchitecture.
**Indicateurs de réussite**
- Nouveau dev opérationnel (première feature livrée) en un temps cible (ex. &lt; 2 jours).
- Dette listée et priorisée (ex. backlog dette avec critères de sortie).
---
## D. Recommandations non évidentes
- **Ne pas nettoyer tout le code arbitraire dun coup.**
Une équipe junior aurait tendance à tout remplacer en un big bang. Mieux vaut : lint qui bloque en *nouveau* code, et migration par zones (layout dabord, puis composants partagés). Les 130 fichiers se traitent par lots avec revue visuelle et régression.
- **Traiter lauth comme un projet de migration, pas comme une tâche.**
Faire un inventaire exhaustif (grep `useAuth` / `useAuthStore`), un plan de migration (ordre des fichiers), des tests E2E et Storybook qui valident login/logout/refresh, puis migrer avec une PR par zone. Un quick fix (tout basculer dun coup) sans tests est risqué.
- **Ne pas activer a11y dans Storybook sans budget de correction.**
Activer axe avec des règles strictes du jour au lendemain va faire exploser les failed”. Mieux : activer sur un sous-ensemble de stories (ex. composants ui/ + layout), corriger ces stories, puis élargir. a11y doit devenir un critère de merge sur les composants critiques.
- **Documenter pourquoi chaque fix-* existe.**
Avant de les supprimer, ajouter un commentaire ou un petit doc : fix-login-form existe parce que …”. Cela évite de recréer le même patch plus tard et guide la refonte (quel composant doit prendre en charge le focus, etc.).
- **Mettre des indicateurs de maturité par feature.**
Une grille simple (loading / empty / error / a11y / tests / Storybook) par feature ou page. Permet de prioriser les remédiations et de ne pas prétendre que tout est à niveau alors que certaines zones sont encore fragiles.
- **Réserver du temps dette dans chaque sprint.**
Un projet correct reporte la dette ; un projet qui vise lexcellence en consacre une part fixe (ex. 20 %). Chaque sprint : X violations arbitraires en moins, Y écrans avec checklist complète, Z stories avec a11y passante.
- **Éviter dajouter de nouveaux stores globaux au fil de leau”.**
Vous avez déjà auth, ui, library, cart, chat. Avant den ajouter un nouveau : vérifier si React Query + état local (ou store de feature) ne suffit pas. Les stores globaux multiplient les sources de vérité et compliquent le debugging et la cohérence.
---
## Synthèse
- **Niveau actuel** : Intermédiaire à senior (6/10), avec de vraies bases (design system, patterns, MSW, tests, Storybook) et des faiblesses structurelles (double auth, violations de layout, correctifs, a11y en option).
- **Écart Discord/Spotify** : Moins une question de stack que de **rigueur dexécution** (une auth, respect des règles, a11y et UX non négociables) et de **finition** (moins de patches, plus de polish).
- **Ordre recommandé** : Fondations (auth, route config, fix-*, lint) Stabilisation (checklist loading/empty/error, a11y, primitives) Polish (Suspense, métriques, micro-interactions) Scalabilité (frontières, state, flags, doc).
- **Ton** : Exigeant mais réaliste : le projet peut viser un niveau world-class à condition de traiter la dette identifiée comme un plan structuré et de ne pas ajouter de nouvelle dette sur lauth et le layout.