veza/apps/web/dev_audit/frontend/05_accessibility_audit.md
senke f64a85414c refactor: Phase 1 — SUMI token foundation
- Rewrite index.css with complete SUMI token system (dark + light themes)
- All --sumi-* variables: backgrounds, surfaces, borders, text, pigments,
  spacing, radius, shadows, glass, scrollbar, motion, z-index, layout
- shadcn/Radix semantic mapping (--background, --foreground, etc.)
- Tailwind @theme mapping with new fonts (Inter, Space Grotesk, JetBrains Mono)
- SUMI keyframe animations (sumi-fade-in, sumi-slide-up, sumi-scale-in, etc.)
- Delete 11 redundant CSS files (design-system.css, design-tokens.css,
  button.css, card.css, input.css, badge-avatar.css, header.css,
  fix-input-focus.css, fix-login-form.css, visual-enhancements.css,
  premium-utilities.css)
- Update main.tsx: single CSS import (index.css only)
- Update ThemeProvider: data-theme attribute instead of .dark class toggle
- Update index.html FOUC script: data-theme attribute

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 01:48:01 +01:00

9.3 KiB

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