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>
13 KiB
| name | overview | todos | isProject |
|---|---|---|---|
| Shell complet et vues full layout | Définir un shell applicatif 100% token-based (sidebar, header, main, player), documenter sa spec, puis ajouter 2–3 stories Storybook "full layout" (App shell placeholder, Dashboard, Playlists) avec Dashboard comme référence visuelle. | false |
Plan : Shell ultra complet + 2–3 vues full layout (Dashboard comme guide)
Contexte
- Le shell actuel est dans DashboardLayout.tsx et Header.tsx mais utilise des valeurs Tailwind en dur (
lg:ml-72,lg:ml-28,pt-20,pb-32,left-72,left-20,h-16) au lieu des tokens déjà définis dans index.css (sidebar, layout primitives). - Les tokens sidebar existent (
.w-sidebar-expanded,.left-sidebar, etc.) mais les marges du main et la position du header ne sont pas en tokens, ce qui crée un écart (ex.ml-72= 18rem alors que--sidebar-width-expanded= 15rem). - Une story DashboardLayout.stories.tsx existe avec du contenu placeholder ; il n’y a pas de story "full layout" avec le vrai DashboardPage ni avec PlaylistListPage.
Phase 1 — Tokens du shell et classes utilitaires
Objectif : Toute dimension du shell (header, main, player) est pilotée par des variables CSS ; plus aucune valeur arbitraire dans le layout.
1.1 Ajouter les variables shell dans index.css
Dans la section "Layout primitives" (après --layout-gap-lg), ajouter :
--header-height: 4rem(équivalent actuelh-16)--main-offset-top: 5rem(équivalentpt-20, espace sous le header fixe)--main-offset-bottom: 8rem(équivalentpb-32, réserve pour le player)--main-margin-left-expanded: 18rem(marge gauche du main quand sidebar ouverte : 15rem sidebar + 3rem gap)--main-margin-left-collapsed: 7rem(marge gauche quand sidebar fermée : 5rem + 2rem gap)--header-left-expanded: 18rem(bord gauche de la barre header quand sidebar ouverte)--header-left-collapsed: 5rem(bord gauche quand sidebar fermée)
Côté main, on garde le padding horizontal en échelle Tailwind (px-4 md:px-8) sauf si tu veux un token dédié plus tard (optionnel).
1.2 Ajouter les classes utilitaires shell dans index.css
Dans la même zone que .left-sidebar / .z-sidebar (layout sidebar), ajouter :
.h-header→height: var(--header-height).pt-main→padding-top: var(--main-offset-top).pb-main→padding-bottom: var(--main-offset-bottom).ml-main-expanded→margin-left: var(--main-margin-left-expanded)(à utiliser aveclg:).ml-main-collapsed→margin-left: var(--main-margin-left-collapsed)(aveclg:).left-header-expanded→left: var(--header-left-expanded).left-header-collapsed→left: var(--header-left-collapsed)
Commente brièvement chaque variable (comme pour --sidebar-width-expanded) pour faciliter la maintenance.
Phase 2 — Remplacer les valeurs en dur dans le layout
Objectif : DashboardLayout et Header n’utilisent plus que des classes basées sur les tokens du shell.
2.1 DashboardLayout.tsx
- Remplacer la
classNamedu conteneur principal (ladivqui aflex-1 flex flex-col...) :lg:ml-72→lg:ml-main-expandedquandsidebarOpenlg:ml-28→lg:ml-main-collapsedquand!sidebarOpen- Conserver
ml-0pour mobile.
- Sur la balise
<main>:pt-20→pt-mainpb-32→pb-main- Garder
px-4 md:px-8et le reste tel quel.
Utiliser cn() pour appliquer ml-main-expanded / ml-main-collapsed selon sidebarOpen (avec préfixe lg:), sans réintroduire de valeurs en dur.
2.2 Header.tsx
- Sur l’élément
<header>:h-16→h-header. - Sur la
divinterne qui positionne la barre (celle avecleft-72/left-20) :left-72→left-header-expandedquandsidebarOpenleft-20→left-header-collapsedquand!sidebarOpen- Conserver
h-16→h-headersur cette même div. - Garder
max-lg:left-0.
Vérifier qu’il n’y a pas d’autres h-16 / left-* liés au shell dans ce fichier.
Phase 3 — Documentation du shell
Objectif : Une seule référence écrite qui décrit le shell et son usage pour les prochaines évolutions.
3.1 Créer docs/APP_SHELL.md
Contenu minimal :
- Vue d’ensemble : rôle du shell (sidebar, header, main, player), fichier principal
DashboardLayout.tsxetHeader.tsx. - Variables CSS : tableau ou liste des variables ajoutées en Phase 1 avec leur valeur et leur rôle (header height, main offset top/bottom, main margin-left expanded/collapsed, header left expanded/collapsed).
- Classes utilitaires : liste des classes (
.h-header,.pt-main,.pb-main,.ml-main-expanded,.ml-main-collapsed,.left-header-expanded,.left-header-collapsed) et quand les utiliser. - Comportement responsive : sur
lg(1024px+) utilisation des marges/positions tokenisées ; en dessous, sidebar en overlay etml-0/left-0pour le header. - Référence croisée : lien vers les tokens sidebar existants dans
index.css(--sidebar-width-expanded, etc.) pour garder une cohérence sidebar ↔ main.
Pas de code dupliqué : le doc pointe vers index.css et les composants, il ne recopie pas tout le CSS.
Phase 4 — Stories full layout dans Storybook
Objectif : Avoir 2–3 vues "app entière" dans Storybook (shell + contenu réel ou réaliste), avec Dashboard comme écran pilote/guide.
4.1 Story "App shell" (placeholder)
- Fichier : DashboardLayout.stories.tsx.
- Conserver ou renommer la story existante Default en AppShellWithPlaceholder (ou garder "Default" et ajouter un sous-titre / description indiquant "Shell with placeholder content").
- Contenu : le même placeholder que aujourd’hui (titre, paragraphe, grille de cartes, liste d’items) pour valider scroll et proportions.
- Paramètres :
layout: 'fullscreen'(déjà présent), viewport optionnel (ex. 1280x720) pour cohérence avec les captures visuelles. - S’assurer que la story utilise bien le décorateur global (aucun décorateur local qui enlève le shell). Aucun changement de décorateur nécessaire si le preview applique déjà
StorybookDecoratorà toutes les stories.
4.2 Story "Dashboard – full layout"
- Même fichier ou fichier dédié selon préférence (recommandation : même fichier
DashboardLayout.stories.tsxpour garder toutes les vues "full layout" au même endroit). - Nouvelle story DashboardFullLayout :
- Rendu :
<DashboardLayout><DashboardPage /></DashboardLayout>. - Passer
parameters.router.initialEntries = ['/dashboard']pour queuseLocation,useNavigateet d’éventuels liens soient cohérents.
- Rendu :
- Dépendances :
DashboardPageutiliseuseUser(),useLibraryItems()/ store library, etc. Les handlers MSW existants (handlers.ts) fournissent déjàauth/meetdashboard. Vérifier que les appels utilisés parDashboardPage(ex. library items si affichés) sont bien mockés, sinon ajouter ou étendre les handlers pour que la page affiche un état "données chargées" et pas un loading infini. - Paramètres :
layout: 'fullscreen', même viewport que la story shell si besoin. - Cette story sert de référence visuelle pour le rendu "Dashboard dans l’app" ; à utiliser pour les comparaisons visuelles et les régressions.
4.3 Story "Playlists – full layout"
- Même fichier ou nouveau fichier (ex.
PlaylistListPage.stories.tsxavec une story full layout qui wrap dansDashboardLayout). Recommandation : une story PlaylistsFullLayout dans DashboardLayout.stories.tsx pour centraliser les vues "app entière".- Alternative propre : créer apps/web/src/features/playlists/pages/PlaylistListPage.stories.tsx (s’il n’existe pas déjà) et y ajouter une story "FullLayout" qui rend
<DashboardLayout><PlaylistListPage /></PlaylistListPage>avecinitialEntries: ['/playlists']. Si tu préfères tout au même endroit, garder dansDashboardLayout.stories.tsx.
- Alternative propre : créer apps/web/src/features/playlists/pages/PlaylistListPage.stories.tsx (s’il n’existe pas déjà) et y ajouter une story "FullLayout" qui rend
- Rendu :
<DashboardLayout><PlaylistListPage /></DashboardLayout>(importerPlaylistListPagedepuis le module playlists). parameters.router.initialEntries = ['/playlists'].- Vérifier les handlers MSW pour les listes de playlists ; ajouter ou adapter si nécessaire pour que la page affiche du contenu (liste ou empty state) et pas un loading infini.
- Paramètres :
layout: 'fullscreen', viewport cohérent.
4.4 Cohérence décorateurs et providers
- Les stories full layout doivent s’exécuter avec le décorateur global (.storybook/decorators.tsx) : ThemeProvider, QueryClient, Toast, Audio, AuthProvider, MemoryRouter. Ne pas dupliquer ces providers dans les stories ; utiliser uniquement
parameters.router.initialEntriespour la route. - Si
DashboardPageouPlaylistListPagedépendent de l’auth Zustand (ex.useAuthStore) en plus de l’AuthProvider, documenter ou mocker l’état auth (ex. via MSW ou un décorateur minimal qui initialise le store) pour que les pages ne redirigent pas vers login. Idéalement, les pages utilisent les mêmes mécanismes qu’en app (AuthProvider + API mockée), pour éviter des écarts de comportement.
Phase 5 — Vérification et régression
- Lint : après les changements, s’assurer qu’aucune règle ESLint n’est cassée sur les fichiers modifiés.
- Storybook : lancer
npm run build-storybook(oustorybook dev) depuisapps/webet ouvrir les stories App shell, Dashboard full layout et Playlists full layout ; vérifier que le shell s’affiche correctement (sidebar, header, main, player), que le contenu scroll et que les données mockées s’affichent. - App réelle : lancer l’app (
npm run dev) et comparer visuellement Dashboard et Playlists avec les mêmes vues dans Storybook pour confirmer l’alignement (pas de décalage de marges, hauteurs, etc.). - Tests existants : exécuter les tests unitaires qui touchent à
DashboardLayoutouHeader(DashboardLayout.test.tsx) et corriger si des classNames ou sélecteurs cassent.
Ordre d’exécution recommandé
- Phase 1 (tokens + classes dans
index.css) - Phase 2 (remplacement dans
DashboardLayout.tsxetHeader.tsx) - Phase 3 (rédaction de
docs/APP_SHELL.md) - Phase 4.1 (story App shell / placeholder)
- Phase 4.2 (story Dashboard full layout + MSW si besoin)
- Phase 4.3 (story Playlists full layout + MSW si besoin)
- Phase 5 (vérifications et tests)
Fichiers impactés (résumé)
| Fichier | Action |
|---|---|
| apps/web/src/index.css | Ajout variables shell + classes utilitaires |
| apps/web/src/components/layout/DashboardLayout.tsx | Remplacer ml/pt/pb par classes token |
| apps/web/src/components/layout/Header.tsx | Remplacer h-16/left-* par classes token |
| apps/web/docs/APP_SHELL.md | Création doc shell |
| apps/web/src/components/layout/DashboardLayout.stories.tsx | Stories App shell, Dashboard full layout, Playlists full layout |
| apps/web/src/mocks/handlers.ts | Ajustements optionnels si endpoints dashboard/playlists/library manquants |
Aucun changement à la logique métier des pages ; uniquement layout, tokens et ajout de stories pour valider le rendu "app entière".