Accessibilité du Frontend
S'applique aux composants dans <ui-packages-dir> et aux pages dans <web-dir>/src/. L'objectif est le niveau WCAG 2.1 AA, et il s'agit d'un plancher, pas d'un objectif.
Résolution des conventions du projet
Les valeurs entre chevrons proviennent du profil de la pile du projet. Résolvez-les dans cet ordre :
- Lisez
STACK-PROFILE.mdà la racine du dépôt et utilisez ses valeurs textuellement. - S'il est absent ou qu'une clé manque, déduisez-les du dépôt —
css-strategyetui-frameworkà partir des dépendances de l'application web. - Si toujours non résolu, conformez-vous à la convention dominante déjà présente dans le dépôt.
- Uniquement lorsque le dépôt est vide, revenez à la valeur par défaut indiquée dans cette compétence — puis écrivez les valeurs choisies dans
STACK-PROFILE.md.
N'inventez jamais une nouvelle convention quand le dépôt en exprime déjà une.
Règles strictes
1. HTML sémantique d'abord
<button>pour les actions,<a>pour la navigation. Jamais de<div>avec un gestionnaire de clic.- Les niveaux de titres descendent sans saut.
<label>pour chaque champ de formulaire, associé avechtmlFor.<fieldset>et<legend>pour les contrôles groupés.- Les points de repère —
<nav>,<main>,<footer>,<aside>— sur chaque page.
2. ARIA uniquement là où HTML ne peut pas
L'ordre de préférence est l'élément sémantique, puis l'attribut ARIA, puis le comportement personnalisé. N'utilisez jamais ARIA pour remplacer la sémantique qu'un élément porte déjà — un role="button" sur une <div> réimplémente, mal, ce que <button> offre gratuitement.
Un ARIA incorrect est pire que pas d'ARIA : un aria-label erroné remplace silencieusement le nom accessible qu'un lecteur d'écran aurait autrement calculé correctement.
3. Le fonctionnement au clavier est obligatoire
- Tout élément interactif est accessible via Tab.
- L'ordre de tabulation suit l'ordre visuel.
- Le focus est toujours visible. Ne supprimez jamais un contour sans le remplacer.
Échapferme les dialogues et popovers ;EntréeetEspaceactivent les contrôles ; les touches fléchées se déplacent dans les widgets composites.- Les widgets personnalisés gèrent le focus explicitement.
4. Couleur et contraste
- 4.5:1 pour le texte normal, 3:1 pour le texte grand (18 px, ou 14 px en gras) et pour les limites d'interface significatives.
- La couleur ne véhicule jamais de sens seule — associez-la à du texte, une icône ou un motif.
- Les deux thèmes sont vérifiés, pas seulement celui par défaut.
5. Images et médias
- Chaque image a un
alt. Descriptif pour le contenu,alt=""pour la décoration. - Jamais « image », « photo » ou le nom du fichier.
- Les images complexes reçoivent une description longue via
aria-describedbyou une légende. - La vidéo comporte des sous-titres ; l'audio comporte une transcription.
6. Formulaires
- Chaque champ est étiqueté.
- Les erreurs sont liées avec
aria-describedbyet annoncées avecrole="alert". - Les champs invalides sont marqués
aria-invalid. - Les champs obligatoires sont marqués avec l'attribut
required— et si un astérisque est affiché, sa signification est précisée textuellement quelque part. - Lors d'un échec de soumission, le focus se déplace vers le premier champ invalide.
7. Taille des cibles
WCAG 2.1 AA n'a pas de taille de cible minimale (2.2 AA fixe 24×24 px CSS ; le chiffre de 44×44 est 2.1 AAA et la convention HIG de la plateforme). Considérez 44×44 comme le plancher pour les actions principales sur les surfaces tactiles, et appliquez-le comme suit :
- Actions principales et destructrices, et tout ce qu'un utilisateur atteint sur un téléphone : 44×44.
- Chrome de bureau dense — barres d'outils, actions de lignes de tableau, contrôles de formulaire compacts — peut être plus petit, jusqu'à 24×24, à condition que les cibles ne soient pas adjacentes à d'autres cibles et qu'un contrôle de taille équivalente existe ailleurs.
- L'espacement compte. Un contrôle de 32 px avec 6 px d'espace libre de chaque côté répond au critère 2.2 ; un contrôle de 32 px collé à son voisin, non.
Les bibliothèques de composants vendues sont généralement en dessous de ce plancher — une hauteur de bouton par défaut de 32 ou 36 px CSS est courante. Ne modifiez pas manuellement le fichier vendu pour le corriger, et ne considérez pas le décalage comme acceptable par défaut. Augmentez la valeur par défaut dans <wrappers-pkg> pour que chaque consommateur hérite de la taille conforme, et conservez les tailles plus petites comme options explicites pour les contextes denses. Ainsi, le choix accessible est celui que vous obtenez sans y penser.
Enregistrez la résolution choisie par le projet dans STACK-PROFILE.md. Une dérogation non documentée est redécouverte comme un bug tous les six mois.
8. Mouvement
- Respectez
prefers-reduced-motion. - Le contenu à lecture automatique dispose d'un contrôle de pause.
- Rien ne clignote plus de trois fois par seconde.
9. Les tests font partie du travail
- Utilisez la fonctionnalité uniquement avec le clavier.
- Écoutez-la avec un lecteur d'écran — VoiceOver sur macOS, NVDA sur Windows.
- Exécutez une analyse automatisée dans la suite de tests de bout en bout.
Les outils automatisés détectent environ un tiers des vrais problèmes. Ils sont un plancher, pas un laissez-passer.
Modèles
Les implémentations complètes se trouvent dans references/component-patterns.md. Ce que chaque modèle garantit :
- Bouton — un
<button>sémantique dont l'anneau de focus apparaît pour les utilisateurs clavier mais pas pour les clics de souris (focus-visible), avec des transitions respectueuses du mouvement et une propriétéariaLabelpour une utilisation avec icône uniquement. - Champ de formulaire — un champ étiqueté dont le texte d'erreur est lié par programme avec
aria-describedby, annoncé avecrole="alert"et signalé avecaria-invalid; l'astérisque obligatoire estaria-hidden. - Dialogue — un
<dialog>natif utilisantshowModal()pour le piégeage gratuit du focus, la gestion deÉchapet l'arrière-plan inerte ; le focus se déplace à l'ouverture et revient au déclencheur à la fermeture. - Liste déroulante — une sélection personnalisée avec navigation par touches fléchées et câblage ARIA correct (
aria-activedescendant,role="listbox") ; construisez-la uniquement lorsqu'un<select>natif ne convient pas. - Lien d'évitement — permet aux utilisateurs clavier de sauter la navigation, visible uniquement lorsqu'il est ciblé ; la cible porte
tabIndex={-1}pour que le focus s'y pose réellement.
Référence rapide WCAG
| Principe | Exigence | En pratique |
|---|---|---|
| Perceptible | Contenu disponible pour tous les sens | Texte alternatif, sous-titres, couleur plus icône |
| Utilisable | Tout est accessible au clavier | Ordre de tabulation, Échap ferme, focus visible |
| Compréhensible | Prévisible, clairement formulé | Navigation cohérente, langage simple |
| Robuste | Fonctionne avec les technologies d'assistance | HTML valide, ARIA correct, éléments sémantiques |
| Problème | Correctif |
|---|---|
| Champ non étiqueté | Ajouter <label htmlFor> |
| Faible contraste | Atteindre 4.5:1, ou 3:1 pour le texte grand |
| Aucun chemin clavier | Éléments interactifs réels, focus géré |
| Alt manquant | Décrire, ou alt="" si décoratif |
| Sens uniquement par la couleur | Ajouter du texte ou une icône |
| Bouton avec icône uniquement | Ajouter aria-label |
| Contenu clignotant | Rester sous trois clignotements par seconde |
Préférences de mouvement
Gérez le mouvement réduit en CSS pur. Une variante de classe utilitaire telle que motion-safe: ne s'applique que lorsque le mouvement est autorisé, donc la placer dans une requête de mouvement réduit produit une règle qui ne peut jamais correspondre.
@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;
}
}
Une valeur proche de zéro plutôt que none permet aux événements de fin de transition de se déclencher, afin que les composants qui les attendent ne bloquent pas.
Pour un contrôle par composant, appliquez la variante motion-safe: à l'utilitaire animé lui-même — motion-safe:transition-colors, comme dans le Bouton ci-dessus.
Contenu visuellement masqué
La plupart des frameworks utilitaires fournissent déjà sr-only et not-sr-only. Utilisez-les ; ne les redéfinissez pas. Si le vôtre ne le fait pas, l'implémentation de référence se trouve dans references/component-patterns.md.
Liste de vérification des tests
Clavier
- Chaque élément interactif accessible via Tab
- L'ordre de tabulation correspond à l'ordre visuel
- Le focus est toujours visible
- Échap ferme les dialogues et popovers
- Les touches fléchées fonctionnent dans les widgets composites
- Le focus revient au déclencheur lorsqu'un dialogue se ferme
Lecteur d'écran
- Tout le contenu est accessible
- Les fonctions des contrôles sont claires sans contexte environnant
- Les labels sont annoncés avec leurs champs
- Les erreurs sont annoncées via
role="alert" - Les points de repère sont présents
- Les éléments décoratifs sont masqués avec
aria-hidden
Couleur
- 4.5:1 pour le texte normal, 3:1 pour le texte grand et les limites d'interface
- Pas de sens uniquement par la couleur
- Les deux thèmes vérifiés
Mouvement
- Mouvement réduit respecté
- Pas de clignotement au-delà de trois par seconde
- Contenu à lecture automatique pouvant être mis en pause
Automatisé
Les analyses sont exécutées dans la suite Playwright — voir frontend-testing. L'extrait Axe-in-Playwright se trouve dans references/component-patterns.md.
Documents de référence
| Analyse approfondie | Référence |
|---|---|
| Implémentations de composants accessibles : bouton, champ de formulaire, modale, liste déroulante, lien d'évitement | references/component-patterns.md |
Compétences connexes
frontend-architecture— couches de composants et thématisationfrontend-testing— où ces vérifications sont exécutéesfrontend-component-docs— documentation du contrat d'accessibilité d'un composant