1. Images (Thématique 1)
Critère 1.1 — Toute image porteuse d'information a une alternative textuelle (Niveau A)
L'attribut alt doit décrire l'information véhiculée par l'image, pas son apparence. Une image décorative doit avoir un alt vide (alt=""), jamais absent.
✅ Correct
<!-- Image informative -->
<img src="graphique-ventes-q3.png"
alt="Graphique des ventes Q3 2024 : +23% vs Q2">
<!-- Image décorative -->
<img src="separateur.svg" alt="" role="presentation">
<!-- SVG inline -->
<svg role="img" aria-labelledby="svg-title">
<title id="svg-title">Logo de l'entreprise Acme</title>
</svg>
❌ Incorrect
<img src="graphique.png"> <!-- alt absent -->
<img src="graphique.png" alt="image"> <!-- alt non descriptif -->
Règle rapide : si vous pouvez retirer l'image sans perte d'information,
alt="". Sinon, décrivez l'information, pas l'apparence.
Critère 1.3 — Description détaillée pour les images complexes (Niveau A)
Pour les graphiques et schémas, une description longue doit compléter le alt court.
✅ Correct
<figure>
<img src="schema-archi.png"
alt="Architecture microservices"
aria-describedby="desc-schema">
<figcaption id="desc-schema">
Ce schéma présente 3 microservices : Auth, API Gateway et Service Métier.
L'Auth communique avec l'API Gateway via JWT...
</figcaption>
</figure>
2. Couleurs (Thématique 3)
Critère 3.2 — Contraste suffisant entre texte et arrière-plan (Niveau AA)
Ratio minimum requis : 4,5:1 pour le texte courant, 3:1 pour le grand texte (≥ 18pt ou ≥ 14pt gras). Cette exigence s'applique aussi aux composants UI et aux éléments graphiques porteurs d'information.
✅ Correct
:root {
--color-text: #1a1a2e; /* ratio 7.1:1 sur blanc ✓ */
--color-placeholder: #6b7280; /* ratio 4.6:1 sur blanc ✓ */
}
button:disabled {
color: #767676; /* 4.54:1 sur blanc ✓ */
background: #e5e5e5;
}
❌ Incorrect
--color-text: #aaaaaa; /* ratio 2.1:1 — insuffisant */
input::placeholder { color: #cccccc; } /* ratio 1.8:1 */
Critère 3.1 — L'information n'est pas transmise uniquement par la couleur (Niveau A)
Un marqueur visuel supplémentaire (icône, bordure, texte) doit toujours accompagner l'information colorée.
✅ Correct
<div class="field-error">
<label for="email">Email</label>
<input type="email" id="email"
aria-describedby="email-error"
aria-invalid="true">
<p id="email-error" role="alert">
⚠ Adresse email invalide
</p>
</div>
.field-error input {
border-color: #dc2626;
border-left-width: 4px; /* marqueur structurel en plus de la couleur */
}
❌ Incorrect
.has-error { border-color: red; } /* seul indicateur d'erreur */
3. Liens (Thématique 6)
Critère 6.1 — Chaque lien a un intitulé explicite (Niveau A)
L'intitulé doit être compréhensible seul, hors contexte. Éviter "cliquez ici", "en savoir plus", "lire la suite".
✅ Correct
<!-- Intitulé descriptif -->
<a href="/rapport-annuel-2024.pdf">
Télécharger le rapport annuel 2024 (PDF, 2 Mo)
</a>
<!-- Complément masqué visuellement -->
<a href="/produits/chaise-oslo">
En savoir plus
<span class="sr-only"> sur la chaise Oslo</span>
</a>
<!-- Lien icône seul : aria-label obligatoire -->
<a href="https://github.com/mon-projet"
aria-label="Code source sur GitHub (nouvelle fenêtre)"
target="_blank" rel="noopener noreferrer">
<svg aria-hidden="true">...</svg>
</a>
/* Classe utilitaire incontournable */
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
Critère 6.4 — Ouverture en nouvelle fenêtre signalée (Niveau A)
✅ Correct
<a href="https://externe.fr" target="_blank" rel="noopener noreferrer">
Documentation officielle
<span aria-hidden="true"> ↗</span>
<span class="sr-only">(ouvre une nouvelle fenêtre)</span>
</a>
4. Formulaires (Thématique 11)
Critère 11.1 — Chaque champ a une étiquette associée (Niveau A)
Le placeholder seul ne suffit pas. Un <label> lié via for/id est obligatoire.
✅ Correct
<!-- Association for/id -->
<label for="nom">Nom <span aria-hidden="true">*</span></label>
<input type="text" id="nom" name="nom" required aria-required="true">
<!-- Groupe radio : fieldset + legend obligatoires -->
<fieldset>
<legend>Méthode de paiement</legend>
<label><input type="radio" name="paiement" value="cb"> Carte bancaire</label>
<label><input type="radio" name="paiement" value="vir"> Virement</label>
</fieldset>
❌ Incorrect
<input type="text" placeholder="Votre nom"> <!-- label absent -->
<label>Email</label><input type="email"> <!-- label non associé -->
Critère 11.10 — Erreurs de saisie accessibles (Niveau AA)
✅ Correct
<label for="pwd">Mot de passe</label>
<p id="pwd-hint">8 caractères minimum, dont 1 chiffre et 1 majuscule</p>
<input type="password" id="pwd"
aria-describedby="pwd-hint pwd-error"
aria-invalid="true">
<p id="pwd-error" role="alert">
Le mot de passe doit contenir au moins 8 caractères.
</p>
<!-- Résumé d'erreurs en haut du formulaire -->
<div role="alert" aria-live="assertive">
<h2>2 erreurs dans le formulaire</h2>
<ul>
<li><a href="#nom">Le nom est obligatoire</a></li>
<li><a href="#pwd">Mot de passe invalide</a></li>
</ul>
</div>
5. Multimédia (Thématique 4)
Critère 4.1 — Vidéo avec audio : sous-titres synchronisés (Niveau A)
✅ Correct
<video controls aria-label="Tutoriel installation Node.js">
<source src="tuto.mp4" type="video/mp4">
<track kind="subtitles"
src="tuto-fr.vtt"
srclang="fr"
label="Français"
default>
<track kind="descriptions"
src="tuto-audiodesc.vtt"
srclang="fr"
label="Audiodescription">
</video>
<p><a href="tuto-transcription.html">Transcription textuelle du tutoriel</a></p>
Important : les contenus audio et vidéo ne doivent pas se lancer automatiquement (critère 4.10), sauf si leur durée est ≤ 3 secondes ou qu'un mécanisme d'arrêt est immédiatement disponible.
6. Tableaux (Thématique 5)
Critères 5.1 à 5.4 — Structure et en-têtes (Niveau A)
✅ Correct
<!-- Tableau simple -->
<table>
<caption>Répartition des tickets par statut – Juin 2024</caption>
<thead>
<tr>
<th scope="col">Statut</th>
<th scope="col">Nombre</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Ouvert</th>
<td>42</td>
</tr>
</tbody>
</table>
<!-- Tableau complexe : id + headers -->
<table>
<caption>Ventes par région et trimestre</caption>
<tr>
<td></td>
<th id="q1" scope="col">T1</th>
<th id="q2" scope="col">T2</th>
</tr>
<tr>
<th id="nord" scope="row">Nord</th>
<td headers="nord q1">12k€</td>
<td headers="nord q2">15k€</td>
</tr>
</table>
❌ Incorrect
<!-- Pas de th, pas de caption -->
<tr><td><strong>Statut</strong></td></tr>
<!-- Table pour la mise en page -->
<table role="presentation">...</table> <!-- ne jamais faire -->
7. Navigation & Focus (Thématique 12)
Critère 12.8 — Focus clavier visible (Niveau AA)
Ne jamais supprimer l'outline sans alternative visible.
✅ Correct
:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 3px;
border-radius: 4px;
}
/* Skip link */
.skip-link {
position: absolute;
top: -100%;
left: 1rem;
background: #1e40af;
color: #fff;
padding: 0.75rem 1.5rem;
border-radius: 0 0 8px 8px;
text-decoration: none;
font-weight: 600;
}
.skip-link:focus { top: 0; }
<a href="#contenu-principal" class="skip-link">Aller au contenu principal</a>
<main id="contenu-principal">...</main>
❌ Incorrect
* { outline: none; } /* suppression globale interdite */
*:focus { outline: 0; }
Critère 12.6 — Landmarks ARIA (Niveau A)
✅ Correct
<body>
<a href="#main" class="skip-link">Aller au contenu</a>
<header role="banner">
<nav aria-label="Navigation principale">...</nav>
</header>
<main id="main" role="main">
<h1>Titre de la page</h1>
<aside aria-label="Articles en relation">...</aside>
</main>
<footer role="contentinfo">
<nav aria-label="Navigation secondaire">...</nav>
</footer>
</body>
8. Scripts & Composants dynamiques (Thématique 7)
Critère 7.1 — Rôles, états et propriétés ARIA (Niveau A)
✅ Correct — Modale
<button type="button" aria-haspopup="dialog" onclick="openModal()">
Ouvrir les paramètres
</button>
<div id="modal" role="dialog" aria-modal="true"
aria-labelledby="modal-title" aria-hidden="true">
<h2 id="modal-title">Paramètres</h2>
<button onclick="closeModal()" aria-label="Fermer les paramètres">×</button>
</div>
function openModal() {
const modal = document.getElementById('modal');
modal.setAttribute('aria-hidden', 'false');
modal.removeAttribute('hidden');
modal.querySelector('button, input, [tabindex]')?.focus();
}
function closeModal() {
const modal = document.getElementById('modal');
modal.setAttribute('aria-hidden', 'true');
document.querySelector('[aria-haspopup="dialog"]')?.focus();
}
✅ Correct — Menu déroulant
<button type="button"
aria-expanded="false"
aria-controls="submenu"
aria-haspopup="true">
Produits
</button>
<ul id="submenu" role="menu" hidden>
<li role="none"><a href="/logiciels" role="menuitem">Logiciels</a></li>
<li role="none"><a href="/services" role="menuitem">Services</a></li>
</ul>
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeMenu();
});
Règle d'or : tout élément non-natif (
div,span) cliquable doit avoir unrole, être focusable (tabindex="0") et répondre au clavier selon les patterns ARIA Authoring Practices.
9. Structure de la page (Thématique 9)
Critère 9.1 — Hiérarchie des titres sans saut (Niveau A)
✅ Correct
<h1>Catalogue produits</h1> <!-- unique par page -->
<h2>Logiciels</h2>
<h3>Licence individuelle</h3>
<h3>Licence entreprise</h3>
<h2>Services</h2>
❌ Incorrect
<h1>Catalogue</h1>
<h3>Logiciels</h3> <!-- saut de niveau interdit -->
<h1>Titre A</h1>
<h1>Titre B</h1> <!-- deux h1 interdits -->
Ne choisissez jamais un niveau de titre pour son rendu visuel. Stylez avec CSS, pas avec le niveau de balisage.
Checklist rapide développeur
| Thématique | Point de contrôle | Niveau |
|---|---|---|
| Images | Chaque <img> a un alt (vide si décorative) | A |
| Images | SVG inline : role="img" + <title> | A |
| Couleurs | Ratio de contraste texte ≥ 4,5:1 | AA |
| Couleurs | Information non transmise uniquement par la couleur | A |
| Liens | Intitulés explicites, compréhensibles hors contexte | A |
| Liens | target="_blank" signalés dans l'intitulé | A |
| Formulaires | Chaque champ a un <label> associé | A |
| Formulaires | Champs en erreur : aria-invalid + message descriptif | AA |
| Formulaires | Groupes radio/checkbox : <fieldset> + <legend> | A |
| Navigation | Focus visible sur tous les éléments interactifs | AA |
| Navigation | Lien d'évitement "Aller au contenu" présent | A |
| Navigation | Landmarks ARIA (main, nav, header, footer) | A |
| Tableaux | <th scope> et <caption> sur les tableaux de données | A |
| Tableaux | Aucune <table> utilisée pour la mise en page | A |
| Scripts | Modales/menus : aria-expanded, aria-haspopup, aria-modal | A |
| Scripts | Navigation clavier complète (Tab, Entrée, Espace, Échap) | A |
| Structure | Hiérarchie h1–h6 sans saut, un seul h1 par page | A |
| Multimédia | Vidéos : sous-titres + transcription textuelle | A |
| Multimédia | Pas d'autoplay sans mécanisme d'arrêt immédiat | AA |
Outils recommandés
- axe DevTools — Extension navigateur + intégration CI/CD via axe-core
- WAVE — Évaluation visuelle en page (extension Chrome/Firefox)
- Colour Contrast Analyser — Outil desktop TPGi pour vérifier les ratios
- NVDA / VoiceOver — Test avec lecteur d'écran (Windows / macOS)
- Tanaguru web ext — Extension navigateur
Documentation basée sur le RGAA 4.1.2 — publié par la DINUM. Référence officielle : accessibilite.numerique.gouv.fr. Le RGAA 5 est attendu pour fin 2026.