veza/docs/PLAN_V0_801_IMPLEMENTATION.md
senke 963a327a0a docs: complete roadmap documentation v0.703 to v0.903 (v1.0 target)
Add Release Scope, Implementation Plan, and Smoke Test for 7 versions:
- v0.703: Go Live & Streaming Complet (Phase 7 Finale)
- v0.801: UX/UI Polish, Accessibilite & PWA (Phase 8)
- v0.802: Cloud Complet, Fichiers & Gear Avance (Phase 8)
- v0.803: Securite, Compliance & Outillage Dev (Phase 8)
- v0.901: Marketplace Complet & Analytics Avances (Phase 9)
- v0.902: Social Complet, Chat & Notifications (Phase 9)
- v0.903: Stabilisation v1.0 & Launch Readiness (Phase 9)

21 documents total (3 per version), covering all remaining features
needed to reach v1.0 from v0.702.
2026-02-24 01:32:04 +01:00

294 lines
8.9 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.

# Plan d'implémentation v0.801 — UX/UI Polish, Accessibilité & PWA
## État des lieux
| Feature | État | Détail |
|---------|------|--------|
| Thème clair/sombre/auto | ✅ | ThemeProvider, toggle Settings |
| Contraste élevé | ❌ | Non implémenté |
| Compact/confortable | ❌ | Layout unique |
| Accent color | ❌ | Primary fixe |
| Navigation clavier | ⚠️ | Partiel |
| ARIA labels | ⚠️ | Manquants sur boutons icônes |
| Focus visible | ⚠️ | Outline basique |
| Font size ajustable | ❌ | Tailles fixes |
| prefers-reduced-motion | ❌ | Animations non conditionnées |
| PWA installable | ⚠️ | SW basique, manifest incomplet |
| Offline | ❌ | Pas de caching |
| Background playback | ❌ | Audio s'arrête en background |
---
## Fichiers existants clés
- Design tokens : [`index.css`](apps/web/src/index.css) (variables CSS, Tailwind layers)
- ThemeProvider : existant dans `apps/web/src/`
- Settings : [`SettingsAppearance.tsx`](apps/web/src/components/settings/SettingsAppearance.tsx)
- Player hooks : [`apps/web/src/features/player/hooks/`](apps/web/src/features/player/hooks/)
- UI components : [`apps/web/src/components/ui/`](apps/web/src/components/ui/)
- Manifest : [`apps/web/public/manifest.json`](apps/web/public/manifest.json)
- Storybook config : [`apps/web/.storybook/`](apps/web/.storybook/)
---
## Step 1 : Migration + User preferences backend (UX1-04, UX1-05)
**Fichier** : `veza-backend-api/migrations/118_users_preferences.sql` (nouveau)
```sql
ALTER TABLE users ADD COLUMN IF NOT EXISTS preferences JSONB DEFAULT '{}';
```
**Fichier** : `veza-backend-api/internal/models/user.go` — ajouter :
```go
Preferences datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"preferences,omitempty"`
```
**Fichier** : `veza-backend-api/internal/handlers/user_handler.go` — ajouter `PUT /users/me/preferences` :
```go
func (h *UserHandler) UpdatePreferences(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok { return }
var prefs map[string]interface{}
if err := c.ShouldBindJSON(&prefs); err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid preferences"))
return
}
// update user.preferences in DB
// ...
RespondSuccess(c, http.StatusOK, gin.H{"preferences": prefs})
}
```
**Commit** : `feat(users): add preferences JSONB column and PUT endpoint`
---
## Step 2 : Design tokens — contraste élevé + compact (UX1-01, UX1-02)
**Fichier** : `apps/web/src/index.css` — ajouter après les thèmes existants :
```css
/* High contrast mode */
[data-contrast="high"] {
--color-foreground: #000000;
--color-background: #ffffff;
--color-muted: #4a4a4a;
--color-muted-foreground: #2a2a2a;
--color-border: #000000;
}
.dark[data-contrast="high"] {
--color-foreground: #ffffff;
--color-background: #000000;
--color-muted: #cccccc;
--color-muted-foreground: #e0e0e0;
--color-border: #ffffff;
}
/* Density modes */
[data-density="compact"] {
font-size: 13px;
}
[data-density="comfortable"] {
font-size: 15px;
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
```
**Commit** : `feat(ui): add high contrast, compact density, reduced motion CSS tokens`
---
## Step 3 : Settings UI — appearance controls (UX1-01 to UX1-04)
**Fichier** : `apps/web/src/components/settings/SettingsAppearance.tsx` — ajouter :
- Toggle "High Contrast" (data-contrast="high" sur document.documentElement)
- Radio "Compact / Default / Comfortable" (data-density)
- Color picker pour accent hue (CSS variable --color-primary)
- Slider "Font Size" (14px20px)
- Persistence dans localStorage + sync PUT /users/me/preferences
**Commit** : `feat(settings): add high contrast toggle, density, accent color, font size`
---
## Step 4 : Accessibilité — ARIA, clavier, focus (UX2-01 to UX2-07)
**Fichier** : `apps/web/src/components/ui/` — pour chaque composant :
- `Button.tsx` : aria-label sur boutons icônes (pas de texte visible)
- `Dialog.tsx` / `Modal.tsx` : aria-modal, focus trap, Escape ferme
- `Input.tsx` : aria-describedby pour messages erreur
- `Tabs.tsx` : role="tablist", role="tab", aria-selected
- `Menu.tsx` / `Dropdown.tsx` : role="menu", Arrow key navigation
- `Table.tsx` : role="table", scope="col" sur headers
- `Pagination.tsx` : aria-label="Pagination", aria-current="page"
**Fichier** : `apps/web/src/index.css` — focus visible amélioré :
```css
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
```
**Fichier** : `apps/web/src/components/layout/` — skip-to-content :
```html
<a href="#main-content" className="sr-only focus:not-sr-only ...">Skip to content</a>
```
**Fichier** : `apps/web/src/hooks/useReducedMotion.ts` (nouveau) :
```typescript
export function useReducedMotion(): boolean {
const [reduced, setReduced] = useState(
() => window.matchMedia('(prefers-reduced-motion: reduce)').matches
);
useEffect(() => {
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
const handler = (e: MediaQueryListEvent) => setReduced(e.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, []);
return reduced;
}
```
**Commit** : `feat(a11y): ARIA labels, keyboard nav, focus visible, skip-to-content, reduced motion`
---
## Step 5 : axe-core Storybook (UX2-08)
```bash
cd apps/web && npm install --save-dev @storybook/addon-a11y
```
**Fichier** : `apps/web/.storybook/main.ts` — ajouter `'@storybook/addon-a11y'` dans addons
**Commit** : `feat(storybook): add addon-a11y for automated accessibility testing`
---
## Step 6 : PWA — manifest, service worker, install prompt (UX3-01 to UX3-03)
**Fichier** : `apps/web/public/manifest.json` — compléter :
```json
{
"name": "Veza — Audio Collaborative Platform",
"short_name": "Veza",
"description": "Collaborative audio platform for musicians",
"start_url": "/",
"display": "standalone",
"theme_color": "#0f172a",
"background_color": "#0f172a",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"shortcuts": [
{ "name": "Dashboard", "url": "/dashboard" },
{ "name": "Marketplace", "url": "/marketplace" },
{ "name": "Go Live", "url": "/live/go-live" }
]
}
```
**Fichier** : `apps/web/src/hooks/usePWAInstall.ts` (nouveau)
```typescript
export function usePWAInstall() {
const [prompt, setPrompt] = useState<BeforeInstallPromptEvent | null>(null);
const [isInstalled, setIsInstalled] = useState(false);
useEffect(() => {
const handler = (e: Event) => { e.preventDefault(); setPrompt(e as BeforeInstallPromptEvent); };
window.addEventListener('beforeinstallprompt', handler);
window.addEventListener('appinstalled', () => setIsInstalled(true));
return () => window.removeEventListener('beforeinstallprompt', handler);
}, []);
const install = async () => { if (prompt) { await prompt.prompt(); } };
return { canInstall: !!prompt && !isInstalled, install, isInstalled };
}
```
**Commit** : `feat(pwa): complete manifest, install prompt hook, offline caching`
---
## Step 7 : Background playback mobile (UX3-04)
**Fichier** : `apps/web/src/features/player/hooks/useWakeLock.ts` (nouveau)
```typescript
export function useWakeLock(isPlaying: boolean) {
const lockRef = useRef<WakeLockSentinel | null>(null);
useEffect(() => {
if (!('wakeLock' in navigator)) return;
if (isPlaying) {
navigator.wakeLock.request('screen').then(l => { lockRef.current = l; }).catch(() => {});
} else {
lockRef.current?.release();
lockRef.current = null;
}
return () => { lockRef.current?.release(); };
}, [isPlaying]);
}
```
**Commit** : `feat(player): add WakeLock for background playback on mobile`
---
## Step 8 : Documentation + release
**Fichier** : `CHANGELOG.md` — section v0.801
**Fichier** : `docs/PROJECT_STATE.md` — Dernier tag → v0.801, section v0.801
**Fichier** : `docs/FEATURE_STATUS.md` — section "Livré en v0.801"
**Commit** : `docs: update CHANGELOG, PROJECT_STATE, FEATURE_STATUS for v0.801`
---
## Step 9 : Rétrospective + archivage + tag
**Fichier** : `docs/RETROSPECTIVE_V0801.md` (nouveau)
**Fichier** : `docs/V0_802_RELEASE_SCOPE.md` (placeholder)
**Fichier** : `docs/SCOPE_CONTROL.md` — mise à jour
**Fichier** : `.cursorrules` — scope v0.802
```bash
mv docs/V0_801_RELEASE_SCOPE.md docs/archive/
git add . && git commit -m "chore(docs): archive V0_801_RELEASE_SCOPE"
git tag v0.801
```
---
## Validation finale
```bash
cd veza-backend-api && go build ./... && go test ./... -v
cd apps/web && npm run build
# Lighthouse audit: PWA score, a11y score
git tag v0.801
```