How to Create Row and Column Headers in HTML Tables
Tables are a great way of presenting detailed and sometimes complicated data in a more comprehensive way, but despite what many developers think, tables are not purely a visual layout - there are certain semantic labels and attributes that different table elements should have so that they can be accurately read by people who use screen readers. A common attribute that is very important in creating accessible tables is correctly labeling column and row headers.
To create a row header, use the scope="row" attribute for table headers on the left of the table. To create a column header, use the scope="col" attribute for table headers along the top of the table.
When to use the “scope” attribute for tables
According to the W3C Web Accessibility Initiative (WIA), using the “scope” attribute is only necessary when a data table has more than one header direction. A common example is a basic comparison table; they typically have one direction of table headers down the columns, and a second direction across the rows, both of which intersect at a table data cell. For basic tables with only one header direction, specifying the “scope” attribute is not necessary.
If you are interested in learning how you can generate a web-accessible and responsive comparison table without having to deal with the grimy details of code, check out the Comparison Table Generator, which is perfect for comparing products, services, offerings, and so much more, or if you are a developer, you might enjoy following my tutorial How to Create a Responsive Product Comparison Table with HTML, where I outline every aspect of a responsive and web-accessible product comparison table.
Why the “scope” attribute is so important for tables
For complex tables that include more than one table header direction, screen readers need to have access to table header “scope” attributes to accurately relay the relationship of the table data to the table headers, otherwise, screen reader users will not understand the context of the table data they are hearing.
Sighted people can easily look at a table layout and without even knowing the semantic meaning of the table elements, they can understand how the table data cells are related to the column and header rows. Sight-impaired people on the other hand rely on assistive technology to read web pages aloud to them, and because screen readers cannot infer the semantic meaning of a table by just looking at it, it needs to have correctly placed table header “scope” attributes so that the user will know how to interpret the table data.
For a more in-depth analysis of web-accessible tables with two headers, you might enjoy reading my article How to Make Product Comparison Tables Web-Accessible. Although it is titled as only relating to product comparison tables, it still applies to any table that has a two header structure.
Creating column headers with scope="col"
Adding a scope= “col” attribute to all of the <th> elements in the table header ensures table data is correctly associated with the respective columns. When a screen reader reads out the table data, it uses this attribute to associate the table cell with the column.
Creating row headers with scope="row"
Adding a scope= “row” attribute to all of the first <th> elements in the body rows ensures table data is correctly associated with the respective rows. When a screen reader reads out the table data, it uses this attribute to associate the table cell with the row.
Example code of a table using scope="row" and scope="col"
<table> <caption>Delivery slots:</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">09:00 – 11:00</th> <td>Closed</td> <td>Open</td> <td>Open</td> <td>Closed</td> <td>Closed</td> </tr> <tr> <th scope="row">11:00 – 13:00</th> <td>Open</td> <td>Open</td> <td>Closed</td> <td>Closed</td> <td>Closed</td> </tr> </table>