Basically, the HTML elements available for defining tables are semantic elements.
Thus, for example, browsers (and other user agents such as screen readers) will recognize directly from the element that a <table> element is actually a table. In the same way, they recognize a table heading by the <th> element and a row of a table by the <tr> element. Nevertheless, even with tables, besides the correct use of the corresponding HTML elements, some additional aspects should be considered with regard to accessibility.
First, you should enclose table headers in a surrounding <thead> element (an exception will be discussed shortly) and surround the individual rows (the data records represented in the table body) with a <tbody> element. In addition, you can optionally add a footer to the table using the <tfoot> element.
Below shows some HTML code for defining an accessible table. This includes the <caption> element, which adds a short description to a table. This information is particularly helpful for users who want to quickly get an idea of what content the table represents. If this element were missing, screen reader users, for example, would first have the contents of some cells read out to them to determine the table’s meaning for themselves.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Users</title>
<meta charset="utf-8">
</head>
<body>
<h1>Users</h1>
<table>
<caption>Users</caption>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr>
<td>Riana</td>
<td>Fresh</td>
<td>District Assurance Producer</td>
</tr>
<tr>
<td>Oscar</td>
<td>Spielvogel</td>
<td>Product Optimization Analyst</td>
</tr>
<tr>
<td>Lynn</td>
<td>Berning</td>
<td>Lead Accountability Administrator</td>
</tr>
<tr>
<td>Carolin</td>
<td>Plass</td>
<td>Investor Usability Strategist</td>
</tr>
<tr>
<td>Claas</td>
<td>Plotzitzka</td>
<td>Chief Implementation Analyst</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Title</th>
</tr>
</tfoot>
</table>
</body>
</html>
The scope attribute, which can be assigned to <th> elements, specify whether the respective table heading refers to the associated column or the associated row (i.e., whether the heading is a vertical or horizontal heading).
By default, a table heading refers to the associated column (so the heading is vertical), which means that, in this case, the scope attribute is optional. Nevertheless, as shown below, a better approach is to explicitly specify the col value anyway to accommodate screen readers and get used to the correct usage directly yourself.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Users</title>
<meta charset="utf-8">
</head>
<body>
<h1>Users</h1>
<table>
<thead>
<tr>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
...
</tbody>
<tfoot>
...
<table/tfoot
</table>
</body>
</html>
However, if the structure of a table requires the definition of table headings that refer to the corresponding table rows (such as the timetable shown in the below figure), you should use the row value for the scope attribute of the corresponding table headings.
The listing below shows the corresponding source code. Notice how both horizontal headings (for defining the times) and vertical headings (for defining the days of the week) are used in this example.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Timetable</title>
<link rel="stylesheet" href="styles.css">
<meta charset="utf-8">
</head>
<body>
<table>
<caption>Timetable</caption>
<tr>
<td></td>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
</tr>
<tr>
<th scope="row">7.55 - 8.40</th>
<td>English</td>
<td>Music</td>
<td>German</td>
<td>Math</td>
<td>PE</td>
</tr>
<tr>
<th scope="row">8.45 - 9.30</th>
<td>English</td>
<td>German</td>
<td>German</td>
<td>Math</td>
<td>PE</td>
</tr>
<tr>
<th scope="row">9.30 - 9.50</th>
<td>Recess</td>
<td>Recess</td>
<td>Recess</td>
<td>Recess</td>
<td>Recess</td>
</tr>
...
</table>
</body>
</html>
Note: Since in horizontal table headers the <th> elements are inside different <tr> elements, they cannot be “grouped” using a <thead> element. So, in this case, omitting the <thead> element is fine (the exception mentioned earlier).
Further information regarding the accessible construction of tables can be found again at the W3C’s WAI website, specifically at https://www.w3.org/WAI/tutorials/tables/. You’ll also find some useful information on what you need to consider with nested table headings.
Editor’s note: This post has been adapted from a section of the book Full Stack Web Development: The Comprehensive Guide by Philip Ackermann.