225 lines
9.3 KiB
Markdown
225 lines
9.3 KiB
Markdown
|
|
# 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 |
|