Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
8.2 KiB
8.2 KiB
Phase E — Accessibility Audit (WCAG 2.1 AA)
Score Accessibilité : 5.5/10
E1. Sémantique HTML
Éléments sémantiques détectés
| Élément | Occurrences | Fichiers |
|---|---|---|
<main> |
1 | components/layout/Layout.tsx |
<nav> |
2 | components/layout/Sidebar.tsx, Navbar.tsx |
<header> |
1 | components/layout/Header.tsx |
<footer> |
0 | Aucun |
<section> |
2 | Rare |
<article> |
0 | Aucun |
<aside> |
0 | Aucun |
Verdict : Sémantique HTML insuffisante pour une application de cette taille. La sidebar devrait être un <aside>, les cards de contenu devraient utiliser <article>, les sections de page <section>. La navigation par landmarks pour les utilisateurs de lecteurs d'écran est limitée. -2 points.
Anti-patterns div onClick
- 2 occurrences de
<div onClick>identifiées :components/social/groups/GroupCard.tsx:66—<div onClick={(e) => e.stopPropagation()}>— pas d'élement interactif, arrêt propagation seulementcomponents/ui/dialog/DialogTrigger.tsx:15—<div onClick={onClick} style={{ display: 'inline-block' }}>— devrait être un<button>
Heading hierarchy
- Hiérarchie définie dans
@layer base[index.css:506-516] : h1→h6 avec tailles responsives ✅ - Non vérifié : L'ordre des headings dans les pages individuelles (h1 → h2 → h3) n'est pas garanti architecturalement. Risque de sauts (h1 → h3).
E2. ARIA
Usage global
- 176 fichiers utilisent des attributs
aria-*✅ Bon niveau d'adoption
Détail
| Attribut | Usage | Fichiers | Verdict |
|---|---|---|---|
aria-label |
Éléments interactifs sans texte visible | Large adoption | ✅ Bon |
aria-live |
Contenus dynamiques | 6+ fichiers (Toast, auth feedback, track list) | ⚠️ Partiel |
aria-expanded |
Menus/accordéons | 3+ fichiers (PlaybackSpeed, QualitySelector, Accordion) | ⚠️ Partiel |
aria-invalid |
Inputs en erreur | input.tsx:31 |
✅ Systématique |
aria-describedby |
Messages d'erreur liés | input.tsx:32 |
✅ |
aria-hidden |
Icônes décoratives | Rarement utilisé | ❌ Manquant |
role="button" |
Éléments cliquables non-boutons | 18 fichiers | ⚠️ Devrait être <button> |
Problèmes identifiés
aria-hiddenabsent sur les icônes Lucide (226 fichiers). Les icônes purement décoratives devraient avoiraria-hidden="true". Violation WCAG 1.1.1.role="button"sur des<div>dans 18 fichiers [PlaylistCard, TrackCard, LibraryPageGrid, etc.] — ces éléments devraient être des<button>natifs pour bénéficier du focus clavier et de la gestion Enter/Space automatique.aria-livelimité — les zones dynamiques (résultats de recherche, listes filtrées, compteurs) n'annoncent pas systématiquement les changements.
E3. Focus management
outline-none sans remplacement
- 45 fichiers utilisent
outline-none - Majorité avec remplacement :
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring— pattern correct ✅ - Exceptions problématiques :
features/studio/components/cloud-file-browser/FileToolbar.tsx—outline-nonesans ring ❌CreateProductViewDetailsCard.tsx,EditProfileIdentityCard.tsx,AccountSettingsPreferencesCard.tsx—outline-nonesur inputs sans ring visible ❌
Focus visible
- 56 fichiers utilisent
focus-visible:✅ - Le composant
Buttonutilisefocus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2[button.tsx:12] ✅ - Le composant
Inpututilisefocus-visible:outline-none focus-visible:ring-2[input.tsx:36] ✅ - Glow effect :
focus-visible:shadow-[var(--sumi-shadow-glow)][button.tsx:12, input.tsx:37] — feedback visuel amélioré ✅
Focus trap
components/ui/focus-trap.tsx(126L) disponible ✅- Utilisé dans
modal.tsxpour les modales ✅ - Vérification manuelle nécessaire : restauration du focus après fermeture de modale
Skip navigation
- ❌ Aucun skip navigation link détecté. Violation WCAG 2.4.1 pour une application avec sidebar + header.
E4. Contraste et couleurs
Estimations de contraste (Dark mode)
| Combinaison | Ratio estimé | WCAG AA (4.5:1) |
|---|---|---|
--sumi-text-primary (#f0ede8) sur --sumi-bg-base (#121215) |
~14:1 | ✅ |
--sumi-text-secondary (#a8a4a0) sur --sumi-bg-base (#121215) |
~8:1 | ✅ |
--sumi-text-tertiary (#706c68) sur --sumi-bg-base (#121215) |
~4.2:1 | ⚠️ Borderline |
--sumi-text-disabled (#4a4844) sur --sumi-bg-base (#121215) |
~2.5:1 | ❌ Insuffisant |
--sumi-accent (#7c9dd6) sur --sumi-bg-base (#121215) |
~7:1 | ✅ |
--sumi-vermillion (#d4634a) sur --sumi-bg-base (#121215) |
~5:1 | ✅ |
Information par couleur seule
- Erreurs : Combinaison couleur rouge + icône AlertTriangle + texte — ✅ pas couleur seule
- Succès : Combinaison couleur verte + icône + texte — ✅
- Toast : Icône + texte + couleur — ✅
E5. Images et médias
Images sans alt
- 2 images sans attribut
alt:components/social/groups/CreateGroupModal.tsx:62—<img src={coverImage}>❌components/upload/metadata/CoverArtUploadModal.tsx:124—<img src={currentImage}>❌
loading="lazy"
- 0 occurrence de
loading="lazy"sur les<img>❌ - Le composant
OptimizedImagegère le lazy loading via IntersectionObserver [optimized-image/OptimizedImage.tsx] mais ce n'est pas la même chose que l'attribut natifloading="lazy".
E6. Formulaires accessibles
Labels
| Critère | Status | Détail |
|---|---|---|
htmlFor + id |
⚠️ Partiel | 56 fichiers — pas systématique |
aria-describedby pour erreurs |
✅ | Input component [input.tsx:32] |
aria-required |
❌ | Pas utilisé systématiquement |
aria-invalid |
✅ | Input component [input.tsx:31] |
autocomplete |
❌ | Pas vérifié systématiquement |
Tableau des violations
| Sévérité | Critère WCAG | Fichier(s) | Problème | Correction |
|---|---|---|---|---|
| 🔴 CRITIQUE | 2.4.1 Bypass Blocks | Global | Pas de skip navigation link | Ajouter <a href="#main-content" class="sr-only focus:not-sr-only">Skip to content</a> |
| 🟠 HAUTE | 4.1.2 Name Role Value | 18 fichiers | role="button" sur div sans keyboard handler |
Remplacer par <button> natif |
| 🟠 HAUTE | 1.1.1 Non-text Content | 226 fichiers | aria-hidden manquant sur icônes décoratives |
Ajouter aria-hidden="true" sur icônes sans label |
| 🟠 HAUTE | 1.3.1 Info Relationships | Global | Sémantique HTML insuffisante (1 <main>, 0 <aside>, 0 <article>) |
Enrichir les landmarks |
| 🟡 MOYENNE | 1.1.1 Non-text Content | CreateGroupModal.tsx:62, CoverArtUploadModal.tsx:124 |
<img> sans alt |
Ajouter alt descriptif |
| 🟡 MOYENNE | 2.4.7 Focus Visible | FileToolbar.tsx + 3 fichiers |
outline-none sans ring de remplacement |
Ajouter focus-visible:ring-2 |
| 🟡 MOYENNE | 4.1.3 Status Messages | Zones dynamiques | aria-live insuffisant pour les résultats de recherche/filtrage |
Ajouter aria-live="polite" sur les zones de résultats |
| 🟢 BASSE | 1.4.3 Contrast | --sumi-text-disabled |
Ratio ~2.5:1 (sous le seuil AA de 4.5:1) | Acceptable pour texte désactivé (non interactif) |
Score Accessibilité détaillé
| Critère | Points | Justification |
|---|---|---|
| ARIA usage | +1.5 | 176 fichiers, bonne adoption |
| Input accessibility | +1 | aria-invalid, aria-describedby, error states |
| Focus visible | +1 | 56 fichiers, glow effect |
| Focus trap modales | +0.5 | Composant dédié |
| Sémantique HTML | -1.5 | 1 main, 0 aside, 0 article — insuffisant |
| Skip navigation | -0.5 | Absent |
| aria-hidden icônes | -0.5 | 226 fichiers sans aria-hidden |
| role="button" sur div | -0.5 | 18 fichiers |
| img alt | -0.25 | 2 images |
| outline-none sans ring | -0.25 | 4 fichiers |
| Contraste | +0.5 | Majoritairement bon |
| Total | 5.5/10 | Efforts visibles mais lacunes structurelles |