Accessibility rule: Table cell missing context, explained
Common Mistakes, Explanations, and Fixes:
- Putting header cells in <td> tags instead of <th>.
- Using scope="row" where scope="col" should be used.
- Leaving a header cell empty.
A table is a grid of labeled columns and rows used to arrange information. Sighted users can make a visual association between table header and data cells of a table. This context gives meaning to the data contained in the table. When navigating through a table with a screen reader, it’s important that the same association is created programmatically. This means that each data point in the table needs to be associated to the right row or column header giving context to the data.
Who is impacted by this barrier?
The issue relates to the use of screen readers in particular. When a screen reader user navigates a table, it should be clear what context every data cell relates to.
How does the check work?
The rule checks that each data cell <td> in a table is assigned to at least one column or row header cell <th>. When this is done, a screen reader can announce each data cell in context.
You can read more about this rule in the technical documentation here.
Note: It is best practice to limit table use to simple data sets. Complex tables are difficult to understand and there may be other, more accessible ways to present the content.
Common Mistakes
Mistake
Putting header cells in <td> tags instead of <th>.
Explanation
Consider a table like this:
<table> <tr><th></th><th>Mon-Fri</th><th>Sat-Sun</th></tr> <tr><td><strong>Summer Hours</strong></td><td>8:00 - 17:00</td><td>10:00 - 14:00</td></tr> <tr><td><strong>Winter Hours</strong></td><td>9:00 - 15:00</td><td>12:00 - 14:00</td></tr> </table>
Mon-Fri | Sat-Sun | |
---|---|---|
Summer Hours | 8:00 - 17:00 | 10:00 - 14:00 |
Winter Hours | 9:00 - 15:00 | 12:00 - 14:00 |
Visually, this table has four headers. “Mon-Fri” and “Sat-Sun” are column headers, and “Summer Hours” and “Winter Hours” are row headers. Visually, it’s clear which cells are the headers, but there is a problem in the HTML with the row headers “Summer Hours” and “Winter Hours.” Because they use <td> (table data) tags instead of <th> (table header), web browsers and assistive technologies have no way of knowing that they’re row headers. Instead, they are seen as regular data cells. This accessibility rule checks that all regular data cells have headers, and of course these ones don’t because they are headers. So, they fail the rule.
Fix
Mark the row headers as such by using <td> for them instead of <th>. In the example above, the fixed HTML would look like this:
<table> <tr><th></th><th>Mon-Fri</th><th>Sat-Sun</th></tr> <tr><th>Summer Hours</th><td>8:00 - 17:00</td><td>10:00 - 14:00</td></tr> <tr><th>Winter Hours</th><td>9:00 - 15:00</td><td>12:00 - 14:00</td></tr> </table>
Mistake
Using scope="row" where scope="col" should be used.
Explanation
The attribute scope="row" marks a cell as a “row header” cell. A “row header” cell describes the rest of the row: that is, all the data cells directly to the right of the row header cell.
scope="col" marks a cell as a “column header” cell. A “column header” cell describes the rest of the column: that is, all the data cells directly below the column header cell.
Consider this table:
<table> <tr> <th scope="row">Mon-Fri</th> <th scope="row">Sat-Sun</th> </tr> <tr> <td>8:00 - 17:00</td> <td>10:00 - 14:00</td> </tr> <tr> <td>9:00 - 15:00</td> <td>12:00 - 14:00</td> </tr> </table>
Mon-Fri | Sat-Sun |
---|---|
8:00 - 17:00 | 10:00 - 14:00 |
9:00 - 15:00 | 12:00 - 14:00 |
Visually, this table has two column headers: “Mon-Fri” and “Sat-Sun.” It has no row headers. However, in the HTML, the column headers have scope="row" on them. That is incorrect and will cause the "Table cell missing context" error to be flagged.
Fix
Option 1: Replace scope="row" with scope="col".
Option 2: Remove scope="row" (and replace it with nothing). This will work because in a simple table like this, browsers and assistive technologies will use the rules of HTML to figure out the scope automatically.
Mistake
Leaving a header cell empty.
Explanation
Consider this table:
<table> <tr> <th>Filename</th> <th></th> <th>Date</th> </tr> <tr> <td>Request for Proposal, Final</td> <td>pdf</td> <td>2023-11-17</td> </tr> <tr> <td>Meeting Minutes</td> <td>docx</td> <td>2023-12-10</td> </tr> <tr> <td>Consulting Rates</td> <td>pdf</td> <td>2023-05-20</td> </tr> <tr> <td>On-call rotation</td> <td>docx</td> <td>2023-11-20</td> </tr> </table>
Filename | Date | |
---|---|---|
Request for Proposal, Final | 2023-11-17 | |
Meeting Minutes | docx | 2023-12-10 |
Consulting Rates | 2023-05-20 | |
On-call rotation | docx | 2023-11-20 |
The "Table cell missing context" error will be flagged on the four cells in the “pdf” / “docx” column. This happens because the column header cell above them is empty.
Fix
Option 1 Fix:
Add some visible text to the column header cell. For example: “File Format.”
<table> <tr> <th>Filename</th> <th>File Format</th> <!-- *NEW* --> <th>Date</th> </tr> <tr> <td>Request for Proposal, Final</td> <td>pdf</td> <td>2023-11-17</td> </tr> <tr> <td>Meeting Minutes</td> <td>docx</td> <td>2023-12-10</td> </tr> <tr> <td>Consulting Rates</td> <td>pdf</td> <td>2023-05-20</td> </tr> <tr> <td>On-call rotation</td> <td>docx</td> <td>2023-11-20</td> </tr> </table>
Filename | File Format | Date |
---|---|---|
Request for Proposal, Final | 2023-11-17 | |
Meeting Minutes | docx | 2023-12-10 |
Consulting Rates | 2023-05-20 | |
On-call rotation | docx | 2023-11-20 |
Option 2 Fix:
Add some visually hidden text to the column header cell.
“Visually hidden” means that the header cell will appear empty to sighted users, but there is some text – “File Format” – in that cell that is perceivable to assistive technologies.
First, you need to add text like “File Format” to the cell, just as “Fix Option 1” did. Then you need to make that text visually hidden by adding the right CSS class to it. Most libraries and frameworks include a "visually-hidden" CSS class. Common names for this class are "visually-hidden" or "sr-only" (“SR” stands for “screen reader”). If you don’t have a CSS class like this already, you can use the one below. (Source: TPGI blog, “The anatomy of visually-hidden”).
.visually-hidden:not(:focus):not(:active) { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }
Then use the "visually-hidden" class on the “File Format” column header:
<table> <tr> <th>Filename</th> <th class="visually-hidden">File Format</th> <!-- *NEW* --> <th>Date</th> </tr> <tr> <td>Request for Proposal, Final</td> <td>pdf</td> <td>2023-11-17</td> </tr> <tr> <td>Meeting Minutes</td> <td>docx</td> <td>2023-12-10</td> </tr> <tr> <td>Consulting Rates</td> <td>pdf</td> <td>2023-05-20</td> </tr> <tr> <td>On-call rotation</td> <td>docx</td> <td>2023-11-20</td> </tr> </table>
Filename | Date | |
---|---|---|
Request for Proposal, Final | 2023-11-17 | |
Meeting Minutes | docx | 2023-12-10 |
Consulting Rates | 2023-05-20 | |
On-call rotation | docx | 2023-11-20 |
Visually, this table looks the same as it did before we fixed the error. But now it contains a visually hidden column header named “File Format.” This header will be exposed to assistive technologies properly, and the “Table cell missing context” rule will pass.
Did you find it helpful? Yes No
Send feedback