RGAA Lab

Composants accessibles

Les tableaux de données

Un tableau se lit de haut en bas et de gauche à droite — sauf quand on ne le voit pas. Lier chaque cellule à ses en-têtes, c'est rendre la grille lisible ligne à ligne. Démo, code, critères RGAA.

8 critères RGAA en jeu

Essayez-les

Un tableau simple

Taux de conformité par thématique, audit de mars 2026
ThématiqueCritères conformesTaux
Images7 sur 978 %
Couleurs3 sur 3100 %
Formulaires9 sur 1369 %

Une double entrée : scope suffit encore

Critères non conformes par thématique et par page auditée
AccueilContact
Images20
Formulaires04

Un tableau complexe : id et headers

Critères par thématique et par page auditée Deux niveaux d'en-têtes en colonne : les pages auditées, puis pour chacune le nombre de critères non conformes et conformes. Les thématiques sont en première colonne, et il n'y a pas de ligne de total.
AccueilContact
Non conformesConformesNon conformesConformes
Images2709
Formulaires01349

Ouvrez le mode tableau de votre lecteur d’écran — Ctrl +

Alt + flèches sur NVDA et JAWS — et parcourez les trois grilles cellule par cellule. À chaque changement de ligne ou de colonne, l'en-tête doit être réannoncé avant la valeur : « *Images, Taux, 78 %* », et non « *78 %* » seul. Réduisez ensuite la fenêtre à 320 pixels, puis tabulez : le troisième tableau déborde, et son cadre s'arrête au clavier pour qu'on puisse le faire défiler aux flèches.

Cette démo existe aussi en page autonome.

L’erreur courante

Le tableau qui a l’air d’un tableau, et n’en est pas un pour le balisage.

<!-- À ne pas faire : rien ici n'est un en-tête -->
<table>
	<tr>
		<td><strong>Thématique</strong></td>
		<td><strong>Taux</strong></td>
	</tr>
	<tr>
		<td>Images</td>
		<td>78 %</td>
	</tr>
</table>

Le gras fait un en-tête pour l’œil et pour personne d’autre. La première ligne est une ligne de données comme les autres, et « 78 % » reste orphelin. <strong> dit l’importance, <th> dit la fonction — la fonction seule est restituée.

Ce que coûte l’erreur se mesure à la lecture linéaire. L’œil recompose la grille en deux dimensions : il remonte à la colonne, revient à la ligne. Cellule par cellule, cette grille n’existe plus, il ne reste qu’une suite de valeurs. Dans un tableau de trois colonnes on compense en mémorisant la position ; dans un tableau de douze, on abandonne.

Deux variantes de la même erreur :

  • Le titre écrit dans un <p> ou un <h3> juste au-dessus du tableau, qu’aucun caption ni aria-labelledby ne relie à lui. Il reste un paragraphe : qui arrive sur le tableau par la navigation entre tableaux ne l’entendra jamais.
  • Le tableau de mise en page qui, lui, emprunte caption et th alors qu’il n’a aucune donnée à décrire, et annonce des étiquettes qui ne veulent rien dire.

Le code, pas à pas

1. Le titre : caption, à privilégier

<table>
	<caption>Taux de conformité par thématique, audit de mars 2026</caption></table>

Le critère 5.4 ne demande pas qu’un tableau ait un titre : il demande que le titre, s’il y en a un, soit associé au tableau. Un tableau qui n’en a aucun ne le fait pas échouer ; un titre visible non associé, si. Mettez-en un tout de même — c’est souvent la seule chose qu’on entend avant de décider d’entrer dans le tableau ou de passer.

Le glossaire « tableau de données ayant un titre » admet quatre porteurs : la balise caption, l’attribut title, aria-label, et aria-labelledby désignant un passage de texte placé avant ou après le tableau. Il ajoute que caption étant seule complètement supportée par les technologies d’assistance, il est « fortement recommandé de privilégier cette solution ». C’est une préférence appuyée, pas la règle : écrivez caption, et gardez aria-labelledby pour le titre qui doit rester hors du tableau.

caption doit être le premier enfant de <table> — placé après un <thead>, il est invalide et parfois ignoré. Et il dit ce que le tableau contient, pas « Tableau 1 » : c’est le critère 5.5.

2. Les en-têtes : th, et scope pour dire ce qu’ils gouvernent

<thead>
	<tr>
		<th scope="col">Thématique</th>
		<th scope="col">Taux</th>
	</tr>
</thead>
<tbody>
	<tr>
		<th scope="row">Images</th>
		<td>78 %</td>
	</tr>
</tbody>

scope="col" dit que l’en-tête gouverne toute sa colonne, scope="row" toute sa ligne. La première cellule d’une ligne est souvent un en-tête elle aussi : « Images » nomme la ligne, ce n’est pas une donnée — et c’est l’oubli le plus fréquent.

Le glossaire « en-tête de colonne ou de ligne » accepte une seconde forme pour les en-têtes qui s’appliquent à la totalité d’une ligne ou d’une colonne : une balise pourvue de role="columnheader" ou role="rowheader". Il recommande dans la foulée de privilégier th, seul balisage totalement supporté. Écrivez donc th — mais un tableau qui emploie le rôle n’est pas non conforme pour autant, et le déclarer tel serait une fausse non-conformité.

3. Le tableau irrégulier : headers et id

La double entrée ne change rien. Tant que chaque en-tête gouverne toute sa ligne ou toute sa colonne, scope suffit : scope="col" et scope="row" associent l’un et l’autre à la cellule qui les croise, et c’est exactement ce que le test 5.7.1 accepte. headers posé là est du zèle — deux techniques concurrentes sur la même grille, et autant d’occasions de casser une association en silence.

Le seuil est ailleurs : l’en-tête qui ne couvre pas toute une ligne ou toute une colonne — en-têtes sur deux niveaux, cellules fusionnées. Sur celui-là, scope devient interdit (test 5.7.3) ; il porte un id unique, et chaque cellule liste dans headers les id de tous ses en-têtes.

<thead>
	<tr>
		<td rowspan="2"></td>
		<!-- Ces deux-là chapeautent deux colonnes chacun : un id, pas de scope -->
		<th colspan="2" id="p-accueil">Accueil</th>
		<th colspan="2" id="p-contact">Contact</th>
	</tr>
	<tr>
		<th id="a-nc">Non conformes</th>
		<th id="a-ok">Conformes</th>
		<th id="c-nc">Non conformes</th>
		<th id="c-ok">Conformes</th>
	</tr>
</thead>
<tbody>
	<tr>
		<th id="t-images">Images</th>
		<td headers="t-images p-accueil a-nc">2</td>
		<td headers="t-images p-accueil a-ok">7</td>
		<td headers="t-images p-contact c-nc">0</td>
		<td headers="t-images p-contact c-ok">9</td>
	</tr>
</tbody>

Une fois headers choisi, tenez-le sur tout le tableau : les en-têtes de ligne passent eux aussi à l’id seul. Mélanger scope ici et headers là ne cumule rien d’utile.

La cellule vide en haut à gauche peut rester un <td> sans contenu : elle ne nomme rien. Un <th> vide y est également admis et courant — ce qu’il ne faut pas, c’est lui inventer un intitulé.

Dernier point, et c’est le piège qui survit aux copiés-collés : un id est unique dans la page, pas dans le tableau. Deux tableaux dupliqués avec les mêmes id d’en-tête associent les cellules du second aux en-têtes du premier.

4. Le résumé, pour un tableau complexe

Le critère 5.1 ne vise ni les tableaux denses ni les doubles entrées : il vise les tableaux de données complexes au sens du glossaire, ceux dont les en-têtes « ne sont pas répartis uniquement sur la première ligne et/ou la première colonne de la grille ou dont la portée n’est pas valable pour l’ensemble de la colonne ou de la ligne ». Une double entrée dont les en-têtes tiennent en première ligne et première colonne n’entre pas dans cette définition, et ne doit aucun résumé. Le tableau de la section précédente, lui, en doit un.

Le glossaire du résumé donne une seule technique utilisable en HTML5 : l’écrire dans la balise caption, à la suite du titre, et le masquer par CSS si nécessaire.

<caption>
	Critères par thématique et par page auditée
	<span class="resume">
		Deux niveaux d'en-têtes en colonne : les pages auditées, puis pour chacune
		le nombre de critères non conformes et conformes. Les thématiques sont en
		première colonne.
	</span>
</caption>

L’attribut summary sur <table> reste accepté par le référentiel, qui le mentionne pour les versions antérieures de HTML. Il est en revanche retiré de HTML5, et les validateurs le signalent comme obsolète : ne l’écrivez pas dans du code neuf, mais ne le déclarez pas non conforme dans du code ancien.

Un résumé visible profite d’ailleurs à tout le monde : la structure d’un tableau à deux niveaux d’en-têtes n’est évidente pour personne.

5. Ce qui rend ce résumé pertinent

Un résumé présent ne vaut rien par sa présence. Trois façons de l’écrire pour rien : redire le titre, commenter les données (« les images sont la thématique la plus en retard »), ou décrire l’apparence (« tableau à fond gris sur trois colonnes »). Aucune n’aide à se déplacer dans la grille.

<!-- À ne pas faire : présent, associé, et sans emploi -->
<span class="resume">Tableau des résultats de l'audit.</span>

Le contrôle tient en une lecture : masquer le tableau, lire le résumé seul, et se demander si l’on saurait s’y déplacer. Un résumé utile dit combien de niveaux d’en-têtes il y a, ce qui est en ligne, ce qui est en colonne, et où sont les totaux quand il y en a.

Le raté le plus discret est le résumé recopié du tableau voisin : exact mot pour mot, faux pour celui-ci. Il coûte plus qu’une absence — on suit une structure qui n’est pas là avant de comprendre qu’on s’est perdu.

6. Le tableau de mise en page : l’exact inverse

Deux critères, deux exigences à ne pas confondre.

Le 5.3 en pose deux, et elles sont cumulatives : le contenu doit rester compréhensible une fois linéarisé — lu cellule après cellule, ligne après ligne — et la balise <table> doit porter role="presentation", ce qui la retire de l’arbre d’accessibilité.

<!-- Une mise en page qui s'assume : le rôle, et rien d'un tableau de données -->
<table role="presentation"></table>

Le 5.8 exige, lui, l’absence de tout marquage de données : ni caption, ni th, ni thead, ni tfoot, ni summary, ni scope, ni headers, ni axis, ni role="columnheader" ou role="rowheader". role="presentation" ne dispense pas de les retirer : le test porte sur le balisage, pas sur ce que le navigateur en fait à l’exécution.

La vraie réponse est ailleurs : ce tableau-là ne devrait pas exister. La grille et le flex font la mise en page sans mentir sur la nature du contenu.

Au clavier

Un tableau de données n’est pas interactif : il n’entre pas dans l’ordre de tabulation, et c’est normal. Sa navigation appartient au lecteur d’écran, qui offre un mode tableau dédié.

ToucheEffet
TabNe s’arrête pas sur les cellules — seulement sur les liens ou boutons qu’elles contiennent, et sur la zone de défilement quand il y en a une.
Ctrl + Alt + flèchesDéplace de cellule en cellule (NVDA, JAWS) ; l’en-tête est annoncé à chaque changement de ligne ou de colonne.
TVa au tableau suivant de la page (mode navigation rapide).

Si un tableau doit défiler horizontalement, la zone qui défile doit pouvoir recevoir le focus — sinon son contenu devient inatteignable au clavier. Un tabindex="0" sur le conteneur suffit, à condition de lui donner un nom : role="region" et aria-labelledby pointant vers le titre du tableau.

Ce que le lecteur d’écran restitue

À l’arrivée sur le tableau : le titre, la nature et les dimensions — « Taux de conformité par thématique, audit de mars 2026, tableau à 3 colonnes et 4 lignes ». Puis, en circulant, chaque cellule est annoncée avec ses en-têtes : « Images, Taux, 78 % ». La formulation varie d’un lecteur à l’autre et selon ses réglages ; ce sont les informations qui doivent être là, pas ces mots-là.

Si vous n’entendez que « 78 % », le lien n’est pas établi — c’est le défaut du critère 5.7. Et si vous n’entendez aucune annonce de tableau, c’est que les <th> manquent : le tableau est traité comme une mise en page.

Checklist

  • Un titre associé au tableau : un caption premier enfant, ou aria-labelledby s’il doit rester hors du tableau.
  • Des <th> pour les en-têtes de ligne et de colonne — jamais un <td> en gras.
  • scope="col" / scope="row" tant que chaque en-tête gouverne toute sa ligne ou toute sa colonne, double entrée comprise.
  • headers + id, sans scope, à partir des en-têtes sur plusieurs niveaux ou des cellules fusionnées.
  • Un résumé de structure dans le caption, pour les seuls tableaux complexes au sens du glossaire.
  • Un résumé qui décrit ce tableau — pas son titre, pas ses chiffres, pas son allure.
  • Un tableau de mise en page porte role="presentation" et aucun marquage de données.
  • Une zone de tableau qui défile horizontalement prend le focus, et porte un nom.

Les critères RGAA en jeu

Vérifiez votre composant avec l'extension

Pour aller plus loin

Sources