veza/apps/web/dev_audit/frontend/05_accessibility_audit.md

225 lines
9.3 KiB
Markdown
Raw Normal View History

# PHASE E — ACCESSIBILITÉ (WCAG 2.1 AA)
---
## E1. Sémantique HTML
### Éléments sémantiques
| Élément | Occurrences | Fichiers |
|---------|-------------|----------|
| `<main>` | 2 | `Layout.tsx:37`, `DashboardLayout.tsx:45` |
| `<nav>` | 2 | `Sidebar.tsx:146` (avec `role="navigation"` + `aria-label`), `Navbar.tsx:88` |
| `<header>` | 1 | `AuthLayout.tsx:48` |
| `<footer>` | 0 | ❌ Absent |
| `<section>` | 0 | ❌ Absent |
| `<article>` | 0 | ❌ Absent |
| `<aside>` | 0 | ❌ Absent |
**Verdict** : ⚠️ La sémantique HTML est **minimale**. Seuls `<main>` et `<nav>` sont utilisés. Aucun `<section>`, `<article>`, `<aside>`, `<footer>` détecté dans les composants. La Sidebar devrait être un `<aside>`, les cards d'articles un `<article>`, les sections de page des `<section>`.
### Anti-patterns
-**Aucun `<div onClick>` ou `<span onClick>` sans role** détecté — excellent
- ✅ Les éléments cliquables utilisent des boutons ou des éléments avec `role` approprié
### Heading hierarchy
- ⚠️ Impossible de valider la hiérarchie h1→h6 sans rendu — les headings sont présents dans les composants mais leur hiérarchie dépend de la composition des pages
---
## E2. ARIA
### Statistiques
| Attribut | Occurrences | Verdict |
|----------|-------------|---------|
| `aria-label` | 100+ | ✅ Bien utilisé |
| `aria-hidden` | 100+ | ✅ Icônes décoratives cachées |
| `aria-describedby` | 15+ | ✅ Messages d'erreur liés |
| `aria-expanded` | 12+ | ✅ Menus/accordéons |
| `aria-live` | 25+ | ✅ Contenus dynamiques annoncés |
| `aria-required` | 6 | ⚠️ Pourrait être plus systématique |
| `aria-pressed` | Présent | ✅ Boutons toggle |
| `aria-current` | Présent | ✅ Navigation |
| `aria-checked` | Présent | ✅ Checkboxes/switches |
| `aria-selected` | Présent | ✅ Tabs/options |
| `aria-disabled` | Présent | ✅ Éléments désactivés |
| `aria-valuemin/max/now` | Présent | ✅ Sliders/progress |
| `aria-invalid` | Présent | ✅ Champs en erreur |
| `aria-busy` | Présent | ✅ Chargements |
| `aria-haspopup` | Présent | ✅ Menus |
| `aria-labelledby` | Présent | ✅ Régions labellisées |
### Roles
| Role | Usage |
|------|-------|
| `button` | Éléments interactifs |
| `navigation` | Sidebar, Navbar |
| `dialog` | Modals/dialogs |
| `alert` | Messages d'alerte |
| `status` | Messages de statut |
| `listbox` / `option` | Select |
| `menuitem` | Dropdown menu |
| `radiogroup` / `radio` | Radio groups |
| `slider` | Sliders |
| `progressbar` | Progress bars |
| `switch` | Toggles |
| `tab` / `tabpanel` / `tablist` | Tabs |
| `search` | Barre de recherche |
| `grid` / `row` | Tables |
| `toolbar` | Barres d'outils |
**Verdict** : ✅ L'usage ARIA est **extensif et correct**. Couverture remarquable.
---
## E3. Focus management
### Outline
| Problème | Fichier:ligne | Détail |
|----------|--------------|--------|
| `outline: none` | `styles/global-effects.css:78,94,107` | Suppression outline |
| `outline: none` | `styles/fix-input-focus.css:18,44` | Suppression outline avec `!important` |
| `outline: none` | `styles/input.css:44` | Suppression outline |
**MAIS** : La plupart des composants TSX utilisent `focus-visible:ring-2 focus-visible:ring-ring` — un indicateur de focus visible est systématiquement ajouté via Tailwind. Les suppressions d'outline dans les CSS sont compensées par des rings visibles dans les composants.
**Verdict** : ⚠️ Techniquement les outlines sont supprimées en CSS, mais remplacées par des focus rings visibles. **Risque** : si un élément interactif n'a pas la classe `focus-visible:ring-*`, il perd tout indicateur de focus.
### Focus trap
-`FocusTrap` composant dédié (`components/ui/focus-trap.tsx`)
- ✅ Utilisé dans `Modal` (`modal.tsx:97`)
- ✅ Utilisé dans `Header` mobile menu (`Header.tsx:138`)
- ⚠️ Non vérifié dans tous les dialogs/drawers
### Focus restauration
- [DONNÉES INSUFFISANTES — nécessite test runtime pour vérifier la restauration du focus après fermeture des modales]
### Skip navigation
-**Absent**. Le test d'accessibilité (`__tests__/accessibility.test.tsx:408-422`) cherche un skip link mais aucune implémentation n'existe dans le code.
- **WCAG 2.4.1 Bypass Blocks** — Violation
### Tab order
-`tabIndex` utilisé correctement : 50+ instances
-`tabIndex={0}` pour éléments focalisables
-`tabIndex={-1}` pour éléments programmatiquement focalisables
- ✅ Conditional tabIndex selon l'état (disabled → -1)
---
## E4. Contraste et couleurs
### Estimation des ratios
| Combinaison | Ratio estimé | Verdict WCAG AA |
|-------------|-------------|-----------------|
| `--foreground` sur `--background` (light) | ~15:1 | ✅ Passe |
| `--foreground` sur `--background` (dark) | ~14:1 | ✅ Passe |
| `--primary` sur `--background` (light) | ~4.5:1 | ⚠️ Limite AA (OKLCH 0.75 sur 0.985) |
| `--primary` sur `--primary-foreground` | ~8:1 | ✅ Passe |
| `--muted-foreground` sur `--background` (light) | ~5:1 | ✅ Passe |
| `--muted-foreground` sur `--muted` (dark) | ~3.5:1 | ⚠️ Limite pour texte normal |
| `--destructive` sur `--background` | ~5:1 | ✅ Passe |
**Note** : Les couleurs OKLCH rendent l'estimation de contraste moins précise. Un test automatisé avec les valeurs rendues est recommandé.
### Information par la couleur seule
- ⚠️ Les badges de succès/erreur/warning utilisent la couleur + une icône — ✅ bon
- ⚠️ Les erreurs de formulaire sont en rouge — nécessitent aussi un texte ou icône
- ✅ Les états actifs utilisent couleur + position/poids typographique
### Mode sombre
- ✅ Complet — toutes les variables CSS ont des valeurs dark mode
- ✅ Contrast ratios ajustés pour le dark mode
- ⚠️ Muted text en dark mode pourrait être sous le seuil WCAG AA (4.5:1)
---
## E5. Images et médias
### Images
-**Toutes les `<img>` ont un attribut `alt`** — aucune image sans alt détectée
- ✅ 40+ images décoratives avec `alt=""` — bonne pratique
- ⚠️ Beaucoup d'images album/artwork ont `alt=""` au lieu d'un alt descriptif — devrait être le titre de l'album/track
### Médias audio
- [DONNÉES INSUFFISANTES — plateforme audio, nécessite vérification des contrôles de lecture et transcriptions]
---
## E6. Formulaires accessibles
### Labels
-`htmlFor` + `id` : 100+ instances correctes
- ✅ La plupart des inputs ont un label associé
- ⚠️ Quelques inputs n'utilisent que des placeholders sans label visible
### Messages d'erreur
-`aria-describedby` : 15+ instances — erreurs liées aux champs
-`aria-invalid` : utilisé sur les champs en erreur
### Required
- ⚠️ `aria-required` : seulement 6 instances — devrait être plus systématique sur tous les champs obligatoires
### Autocomplete
-`autoComplete` : 10+ instances sur les champs auth (email, password, name)
- ✅ Bonne pratique sur les formulaires d'authentification
---
## Tableau des violations
| Sévérité | Critère WCAG | Localisation | Problème | Correction |
|----------|-------------|--------------|----------|------------|
| 🔴 CRITIQUE | 2.4.1 Bypass Blocks | App-level | Skip navigation absent | Ajouter `<a href="#main" class="sr-only focus:not-sr-only">` |
| 🟠 HAUTE | 1.3.1 Info & Relationships | Toute l'app | Aucun `<section>`, `<article>`, `<aside>`, `<footer>` | Ajouter landmarks sémantiques |
| 🟠 HAUTE | 1.1.1 Non-text Content | Artwork images | `alt=""` sur images de contenu (albums, tracks) | Ajouter alt descriptif (titre album/track) |
| 🟡 MOYENNE | 2.4.7 Focus Visible | `styles/global-effects.css`, `fix-input-focus.css` | Outline supprimé en CSS | S'assurer que TOUS les éléments interactifs ont `focus-visible:ring-*` |
| 🟡 MOYENNE | 1.4.3 Contrast | Dark mode muted text | `--muted-foreground` potentiellement sous 4.5:1 | Augmenter la luminosité à oklch(0.75+) |
| 🟡 MOYENNE | 3.3.2 Labels | Formulaires divers | `aria-required` sur seulement 6 champs | Ajouter sur tous les champs obligatoires |
| 🟢 BASSE | 4.1.3 Status Messages | Toasts | `aria-live` présent mais pas sur tous les toasts | Vérifier que tous les toasts ont `role="alert"` |
| 🟢 BASSE | 1.3.5 Input Purpose | Certains formulaires | `autoComplete` manquant sur certains inputs | Ajouter `autoComplete` sur champs d'adresse, téléphone |
---
## SCORE ACCESSIBILITÉ : 6.5/10
### Points gagnés
| Point | Score | Justification |
|-------|-------|---------------|
| ARIA extensif | +1.5 | 200+ attributs ARIA, roles corrects, aria-live |
| Focus management | +1.0 | FocusTrap, focus-visible rings, tabIndex correct |
| Form labels | +1.0 | htmlFor/id, aria-describedby, autoComplete |
| Images alt | +0.8 | 100% des images ont alt |
| Icônes aria-hidden | +0.7 | 100+ instances correctes |
| prefers-reduced-motion | +0.5 | 3 instances de respect |
| ESLint jsx-a11y | +0.5 | Plugin actif |
### Points perdus
| Point | Score | Justification |
|-------|-------|---------------|
| Skip navigation absent | -1.0 | Violation WCAG 2.4.1 critique |
| Sémantique HTML minimale | -1.0 | Pas de section, article, aside, footer |
| Images artwork sans alt descriptif | -0.5 | album/track covers avec alt="" |
| Outline CSS supprimé | -0.3 | Compensé par focus rings mais risque |
| aria-required insuffisant | -0.2 | Seulement 6 champs |
| Contraste dark mode muted | -0.5 | Potentiellement sous 4.5:1 |