A11y Front-end Roadmap #7 - Tables

A11y Front-end Roadmap #7 - Tables

Presenting data in tabular way has never been so accessible!

Go back to A11y Roadmap Index

Tables can be quite common if you're dealing with informative content. Tables use many different tags, but if implemented correctly, they require no special JavaScript work to be accessible. A word of advice: tables are for data presentation, nothing more. If you need a tabular layout for data, buttons, widgets, and so on, what you need is a “grid,” which is visually similar to a table but more complex in terms of JavaScript and keyboard interaction.

1. Basic Table

A <table> element contains a set of <tr> (table rows), and every row is divided into cells, containing a header with <th> (table header) or normal data with <td> (table data). Usually, headers refer to columns, and their scope is the underlying column (attribute scope="col"). In its simplest form, a table could look like this:

1.1 Example

<table>
    <tr><th>Name</th><th>Age</th></tr>
    <tr><td>Will</td><td>34</td></tr>
    <tr><td>Jill</td><td>42</td></tr>
</table>

2 . Intermediate Table

We’ve seen a “single-entry” table, but sometimes we need horizontal headers. We can use table headers to identify rows as well; in these cases, we need to specify that our <th> scope is row. The cell at the intersection of column headers and row headers can be a header itself or, if not needed, a simple <td>.

1.2 Example

<table>
    <tr><td></td><th>First Semester</th><th>Second Semester</th></tr>
    <tr><th scope="row">2024</th><td>16</td><td>74</td></tr>
    <tr><th scope="row">2025</th><td>12</td><td>65</td></tr>
</table>

3. Table Regions

For simple or intermediate tables, it is not mandatory to use the following table regions, but applying them is good practice: it will make your layout more robust and clearer for the developer. These regions include:

  • <thead> (table head): groups all the headers.

  • <tbody> (table body): groups rows together to indicate a certain relationship among them. You can have more than one <tbody>, but it's also fine to have only one.

  • <tfoot> (table footer): contains rows that summarize all your columns.

1.3 Example

<table>
    <thead>
        <tr><th>Years</th><th>Earnings</th><th>Expenses</th></tr>
    </thead>
    <tbody>
        <tr><th scope="row">2024</th><td>100</td><td>65</td></tr>
        <tr><th scope="row">2025</th><td>56</td><td>33</td></tr>
    </tbody>
    <tfoot>
        <tr><td scope="row">Total</td><td>156</td><td>98</td></tr>
    </tfoot>
</table>

4. Describe Your Table

Try to avoid presenting your table in a “too dry” way. If possible and helpful for the user, add a title and a description. Tables have their own tag for describing their content: <caption>. Keep in mind that if present, the <caption> element must be the table's first child. However, if you want it to appear below the table, you can style it with the CSS property caption-side, which can be set to top or bottom (or other secondary values).

If you need a more extensive description of how data is organized in your table, you can use a summary. Unfortunately, no “summary” tag exists, so you must find a creative way to represent this. Here are two possible solutions:

  1. Add a <span> inside the <caption>, styled as secondary importance text (with a class like .caption__summary).

     <table>
         <caption>My table name. <span class="caption__summary">A more complete description.</span></caption>
         <thead>...</thead>
         <tbody>...</tbody>
     </table>
    
  2. Use an <h2> and <p>, each with an id to reference the <table> with aria-labelledby and aria-describedby attributes.

     <h2 id="table__title">My table name</h2>
     <p id="table__summary">A more complete description.</p>
     <table aria-labelledby="table__title" aria-describedby="table__summary">
         <thead>...</thead>
         <tbody>...</tbody>
     </table>
    

5. Complex Tables

Tables can become increasingly complex. We can have headers or cells that span multiple columns or rows, or sub-headers in the middle of a long table. However, for this brief introduction, what we have covered should be sufficient to help you tackle the most common kinds of data tables. You can find more advanced examples and tags (like <col> and colspan) in the article Tables - A full tour (coming soon).

A11y Front-end Roadmap Table of contents