4 novembre 2026
Tableau accessible HTML : th, scope, headers et caption expliqués avec du code
Un tableau accessible HTML repose sur un principe simple : chaque cellule d'en-tête est balisée avec <th>, chaque cellule de données avec <td>, et la relation entre les deux est définie par le balisage ; pour les tableaux plus complexes, des associations explicites peuvent être nécessaires. C'est ce que rappelle le tutoriel tableaux du W3C (WAI) : les technologies d'assistance s'appuient sur ce balisage pour donner du contexte à l'utilisateur. Selon la complexité du tableau, vous utiliserez en plus l'attribut scope, ou les attributs id et headers, et, dans la plupart des cas, un élément <caption> pour donner un titre au tableau. Ce guide reprend chaque cas avec des exemples de code prêts à adapter : tableau à un seul en-tête, tableau à en-têtes de lignes et de colonnes, en-têtes fusionnés, tableau complexe à plusieurs niveaux, légende et résumé, puis les erreurs à ne pas commettre.
Pourquoi le balisage d'un tableau accessible HTML est indispensable
Un tableau sans balisage structurel, qui ne distingue pas les cellules d'en-tête des cellules de données et ne les relie pas correctement, crée des obstacles. Se fier uniquement aux indices visuels ne suffit pas : il faut que les en-têtes et les données puissent être déterminés par programme.
Concrètement, un lecteur d'écran lit une cellule à la fois et annonce les en-têtes de ligne et de colonne qui lui sont associés : l'utilisateur ne perd pas le contexte en se déplaçant dans le tableau. D'autres personnes affichent les données autrement, par exemple avec des feuilles de style personnalisées qui font ressortir les en-têtes, changent la taille du texte et les couleurs, ou présentent l'information sous forme de liste plutôt que de grille. Ces restitutions alternatives ne fonctionnent que si le code du tableau est correctement structuré.
Côté WCAG, le tutoriel du W3C rattache ces pratiques au critère de succès 1.3.1 Information et relations (niveau A) et aux techniques H51 (utiliser le balisage de tableau pour présenter une information tabulaire), H63 (utiliser l'attribut scope pour associer les cellules d'en-tête aux cellules de données dans les tableaux de données) et H39 (utiliser l'élément caption pour associer une légende à un tableau de données).
- Cellules d'en-tête : <th>.
- Cellules de données : <td>.
- Tableaux plus complexes : des associations explicites avec scope, id et headers peuvent être nécessaires.
- Ces règles concernent les tableaux de données, pas les tableaux de mise en page.
Tableau simple : <th> pour les en-têtes et scope pour lever l'ambiguïté
Le cas le plus simple est un tableau avec un seul en-tête, soit sur la première ligne, soit dans la première colonne, et des données qui se distinguent clairement d'une colonne à l'autre. Pour un petit tableau de concerts (date, événement, lieu), baliser la première ligne avec <th> et le reste avec <td> n'est acceptable que parce que le tableau est petit et que les données de chaque colonne sont nettement différentes, ce qui rend la relation entre en-têtes et données évidente. Le W3C signale toutefois que certains lecteurs d'écran liront « Date – Event – Venue » dans la cellule « Venue », car la direction des <th> est ambiguë.
Dès que les données ne se distinguent plus d'elles-mêmes, ajoutez l'attribut scope. Dans l'exemple du W3C, une liste de collectionneurs avec nom, prénom et ville : sans savoir à quel en-tête correspond chaque cellule, impossible de distinguer un nom d'un prénom ou d'une ville. La valeur col de scope définit la direction des en-têtes et les associe aux cellules de données de leur colonne. Le W3C précise que scope est aussi nécessaire pour les tableaux plus grands qui n'ont qu'une ligne ou une colonne d'en-têtes.
<table>
<caption>Collectionneurs d'ours en peluche</caption>
<tr>
<th scope="col">Nom</th>
<th scope="col">Prénom</th>
<th scope="col">Ville</th>
</tr>
<tr>
<td>Phoenix</td>
<td>Imari</td>
<td>Henry</td>
</tr>
[…]
</table>En-têtes de lignes et de colonnes : scope="col" et scope="row"
Lorsqu'un tableau a à la fois un en-tête de colonnes et un en-tête de lignes, la relation entre en-têtes et données devient vite ambiguë. Utilisez <th> pour toutes les cellules d'en-tête et déclarez la direction de chacune : scope="col" indique que l'en-tête s'applique à toute la colonne, scope="row" à toute la ligne.
L'exemple du W3C est un tableau de créneaux de livraison : les jours de la semaine en haut, les plages horaires à gauche, et dans les cellules « Ouvert » ou « Fermé ». Ces mentions n'ont de sens que si l'on peut les rattacher à un jour et à une heure précis. La cellule en haut à gauche, vide, reste un <td>.
La même technique fonctionne quand les en-têtes de lignes ne sont pas dans la première colonne. Dans un tableau de congés où la première colonne contient un identifiant et la deuxième le nom de la personne, le nom est balisé <th scope="row"> : il s'applique alors aux cellules situées à sa gauche comme à sa droite.
<table>
<caption>Créneaux de livraison</caption>
<tr>
<td></td>
<th scope="col">Lundi</th>
<th scope="col">Mardi</th>
</tr>
<tr>
<th scope="row">09:00 – 11:00</th>
<td>Fermé</td>
<td>Ouvert</td>
</tr>
[…]
</table>En-têtes fusionnés : colgroup, rowgroup, thead et tbody
Quand une cellule d'en-tête s'étend sur plusieurs colonnes (colspan) ou plusieurs lignes (rowspan), elle doit être associée aux cellules correspondantes du groupe. On utilise pour cela scope="colgroup" ou scope="rowgroup". Mais avant, la structure des groupes doit être définie dans le balisage.
Pour les colonnes, chaque colonne est identifiée par un élément <col> en partant de la gauche ; si un en-tête couvre plusieurs colonnes, un <colgroup> avec un attribut span remplace ce nombre d'éléments <col>. Le total des <col> (hors <colgroup>) et des colonnes indiquées par les span des <colgroup> doit être égal au nombre total de colonnes du tableau. Pour les lignes, un groupe est défini par <thead>, <tbody> ou <tfoot>.
- <thead> et <tfoot> peuvent être utilisés une fois par tableau.
- Un tableau peut contenir autant de <tbody> que nécessaire, chacun définissant un groupe de lignes.
- Si un en-tête couvre plusieurs lignes d'en-tête, ces lignes sont regroupées dans <thead> au lieu de <tbody> ; s'il couvre plusieurs lignes du pied de tableau, on utilise <tfoot>.
- Le W3C note qu'utiliser <thead>, <tbody> et <tfoot> dans tous les tableaux peut éviter les hésitations sur le moment de les employer.
<table>
<col>
<colgroup span="2"></colgroup>
<colgroup span="2"></colgroup>
<tr>
<td rowspan="2"></td>
<th colspan="2" scope="colgroup">Mars</th>
<th colspan="2" scope="colgroup">Vénus</th>
</tr>
<tr>
<th scope="col">Fabriqués</th>
<th scope="col">Vendus</th>
<th scope="col">Fabriqués</th>
<th scope="col">Vendus</th>
</tr>
<tr>
<th scope="row">Ours en peluche</th>
<td>50 000</td>
<td>30 000</td>
<td>100 000</td>
<td>80 000</td>
</tr>
[…]
</table>Tableau complexe : associer chaque cellule avec headers et id
Certains tableaux sont trop complexes pour qu'on puisse associer en-têtes et données de façon strictement horizontale ou verticale. Le W3C cite deux cas : les tableaux dont les en-têtes de colonnes se répètent ou changent en cours de tableau, et ceux où trois en-têtes ou plus sont associés à chaque cellule de données.
La méthode : chaque <th> reçoit un id unique dans tout le document, et chaque <td> liste dans son attribut headers le ou les id des en-têtes qui le concernent, séparés par des espaces. Dans l'exemple du W3C sur la disponibilité de logements de vacances, la cellule « 11 » est reliée à la fois à la ville (Paris), au nombre de chambres (1 chambre) et au type de logement (studio).
Astuce tirée du tutoriel : dans un tableau de contacts fournisseurs où les en-têtes changent à mi-tableau, les cellules d'en-tête elles-mêmes pointent vers une cellule vide portant l'id « blank », ce qui empêche certaines technologies d'assistance de lire un en-tête pour ces cellules d'en-tête.
Avant de vous lancer, demandez-vous si le tableau doit vraiment rester aussi complexe. Le W3C suggère, dans bien des cas, de le scinder en tableaux plus petits : l'information devient plus facile à comprendre pour tout le monde, plus facile à coder, et les tableaux simples sont bien mieux pris en charge par les outils de création de contenu, y compris les éditeurs WYSIWYG.
[…]
<tr>
<th id="par" colspan="5" scope="colgroup">Paris</th>
</tr>
<tr>
<th id="pbed1">1 chambre</th>
<td headers="par pbed1 stud">11</td>
<td headers="par pbed1 apt">20</td>
[…]
</tr>Légende avec <caption> et résumé des tableaux complexes
La légende fonctionne comme un titre pour le tableau. La plupart des lecteurs d'écran annoncent son contenu ; elle aide l'utilisateur à trouver le tableau, à comprendre son sujet et à décider s'il veut le lire. Pour les personnes qui naviguent en « mode tableaux », de tableau en tableau, la légende est le principal moyen de les identifier. Elle se code avec l'élément <caption>, placé directement comme enfant de <table>, et doit rester un titre court, par exemple « Concerts ».
Le résumé, lui, décrit l'organisation des données et aide à naviguer ; il n'est généralement utile que pour les tableaux complexes. Si vous fournissez les deux, le résumé ne doit pas répéter une information déjà présente dans la légende. Le W3C rappelle que légende et résumé ne sont pas exigés dans tous les cas pour respecter les WCAG, mais qu'ils sont des moyens assez simples de fournir une information souvent nécessaire.
- Résumé dans <caption> : le texte suit le titre, par exemple dans un <span>, et il est aussi visible pour les utilisateurs voyants.
- Résumé lié par aria-describedby sur <table>, qui pointe vers n'importe quel élément doté d'un id unique, par exemple un paragraphe. Cet élément n'a pas besoin de précéder le tableau, mais le placer à proximité aide à découvrir le résumé plus vite, surtout pour les personnes qui n'utilisent pas de lecteur d'écran. Le W3C prévient que cette fonctionnalité WAI-ARIA peut être moins bien prise en charge par les technologies d'assistance.
- Tableau dans <figure> avec <figcaption> : en « mode tableaux », les utilisateurs de lecteurs d'écran ne parviennent généralement pas à identifier le tableau par une légende placée ainsi. La partie légende de <figcaption> peut être associée explicitement au tableau avec aria-labelledby, et la partie résumé avec aria-describedby, ce qui peut entraîner plusieurs lectures de la légende et du résumé. Le W3C prévient aussi que cette fonctionnalité HTML5 peut être moins bien prise en charge par les technologies d'assistance.
<table>
<caption>Disponibilité des logements de vacances<br>
<span>La première colonne indique la ville et la taille du logement, les autres colonnes le type et le nombre de logements disponibles</span>
</caption>
[…]
</table>Ce qu'il ne faut pas faire dans un tableau HTML
La page « Tips and Tricks » du tutoriel regroupe plusieurs pratiques à éviter.
- Styliser des <td> pour qu'ils ressemblent à des en-têtes : utilisez <th>, sinon le tableau devient moins accessible, voire inaccessible. Il est aussi utile de distinguer visuellement <th> et <td>.
- Enchaîner plusieurs tableaux dans un seul en ajoutant une ligne de <th> : les lecteurs d'écran peuvent lire toutes les cellules <th> d'une colonne. Commencez un nouveau <table> quand le sujet change.
- Mettre les en-têtes dans une colonne et toutes les données dans une seconde : chaque donnée doit avoir sa propre cellule.
- Créer des pseudo-lignes avec des <br> : les données de ces pseudo-lignes risquent de ne plus s'aligner correctement quand la taille du texte change.
- Couper le tableau sur petit écran ou en zoom, par exemple avec overflow: hidden en CSS. Le tutoriel applique overflow: scroll à un élément qui enveloppe le tableau, mais il existe bien d'autres options d'affichage.
- Perdre la structure en version responsive : la relation entre en-têtes et données doit rester disponible dans tous les formats.
- Utiliser un tableau pour la mise en page : préférez CSS. Si un tableau de mise en page existe déjà, n'y utilisez ni les éléments structurels (comme <th> ou <caption>) ni les attributs présentés dans ce tutoriel (scope, headers…) et ajoutez role="presentation" sur <table>.
Questions fréquentes
Comment rendre un tableau HTML accessible ?
Balisez les cellules d'en-tête avec <th> et les cellules de données avec <td>. Ajoutez scope="col" ou scope="row" quand la direction des en-têtes est ambiguë, scope="colgroup" ou scope="rowgroup" pour les en-têtes fusionnés, id et headers quand l'association ne peut pas être strictement horizontale ou verticale (en-têtes qui changent en cours de tableau, trois en-têtes ou plus par cellule). Dans la plupart des cas, donnez aussi un titre au tableau avec <caption> : ce n'est pas exigé dans tous les cas par les WCAG, mais c'est utile.
Quelle est la différence entre scope et headers ?
scope indique la direction d'un en-tête : col, row, colgroup ou rowgroup. Il suffit tant que l'association est strictement horizontale ou verticale. headers, placé en général sur une cellule de données, liste explicitement les id des en-têtes associés ; il sert quand les en-têtes changent en cours de tableau ou quand trois en-têtes ou plus concernent chaque cellule.
La balise caption est-elle obligatoire dans un tableau ?
Selon le W3C, légende et résumé ne sont pas exigés dans tous les cas pour respecter les WCAG. La légende est cependant utile dans la plupart des situations : la plupart des lecteurs d'écran l'annoncent et elle est le principal moyen d'identifier un tableau en mode tableaux.
Faut-il mettre scope sur un tableau simple ?
Pas toujours : des <th> sans scope ne sont acceptables que pour un petit tableau dont les données se distinguent clairement d'une colonne à l'autre ; même dans ce cas, certains lecteurs d'écran liront « Date – Event – Venue » dans la cellule « Venue », car la direction des <th> est ambiguë. Dès que les données sont ambiguës, ou pour un tableau plus grand avec une seule ligne ou colonne d'en-têtes, ajoutez scope.
Peut-on utiliser un tableau HTML pour la mise en page ?
Non, le W3C recommande CSS pour la mise en page. Pour un tableau de mise en page déjà en place, n'utilisez ni les éléments structurels comme <th> ou <caption>, ni les attributs vus dans ce guide (scope, headers…), et ajoutez role="presentation" sur l'élément <table>.
À lire aussi sur Conformly
Sources
Article appuyé sur les sources officielles citées en fin de page.