Layout Table With Data-Table Markup
ID |
html.layout_table_semantic_markup |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Html |
Tags |
accessibility, reliability |
Description
Flags a table that declares itself layout and then carries the markup only a data table has.
Rationale
role="presentation" (and its synonym role="none") takes the table out of the accessibility tree:
a screen reader reads the cells as a plain run of content and never announces a row or column
position. That is the right declaration for a table used to place things on a page. Data-table
markup on such a table is a contradiction the author has not noticed - either the role is wrong and
the table really presents data, in which case the role is hiding its structure from every assistive
technology, or the markup is left over from a data table it used to be and is now dead weight that
the next reader will trust.
<table role="presentation"> <!-- FLAW - a header cell in a layout table -->
<tr><th>Menu</th><td>Content</td></tr>
</table>
<table role="none" summary="Page layout"> <!-- FLAW - summary describes a data table -->
<tr><td>Menu</td><td>Content</td></tr>
</table>
<table role="presentation"> <!-- FLAW - so does a caption -->
<caption>Layout</caption>
<tr><td>Menu</td><td>Content</td></tr>
</table>
<table role="presentation"> <!-- FLAW - scope belongs to a header cell -->
<tr><td scope="row">Menu</td><td>Content</td></tr>
</table>
<table role="presentation" summary=""> <!-- OK - the legacy way to say "layout" -->
<tr><td>Menu</td><td>Content</td></tr>
</table>
<table role="presentation"> <!-- OK - cells and nothing else -->
<tr><td>Menu</td><td>Content</td></tr>
</table>
<table> <!-- OK - a data table, where it belongs -->
<caption>The team</caption>
<tr><th scope="col">Name</th><th scope="col">Role</th></tr>
<tr><td>Ada</td><td>Owner</td></tr>
</table>
The criteria are deliberately narrow on both sides.
The table has to say it is layout - role="presentation" or role="none". The rule never infers
that from a missing header row, because a missing header row is the very thing
html.table_headers asks for: inferring it would make one piece of markup a finding of both rules,
with opposite advice. A table that declares no role and does not read as a grid of data is judged by
neither.
The markup asked about is the four things that exist only to describe a grid of data: a <th> header
cell, a <caption>, a summary describing the table in prose, and scope on a cell. An empty
summary is the opposite signal - it is how HTML 4 pages declared a table to be layout - so it is
not a finding. Only the table’s own markup counts: a data table nested inside a layout wrapper keeps
its own caption and headers and is judged as the data table it is.
Remediation
Decide which kind of table it is. If it lays out the page, drop the data-table markup - and better still, replace the table with CSS grid or flex layout, which needs no role to be understood:
<table role="presentation">
<tr><td>Menu</td><td>Content</td></tr>
</table>
If it presents data, drop the role and finish the job: a caption, a header row marked with <th>,
and a scope on each header.
<table>
<caption>The team</caption>
<tr><th scope="col">Name</th><th scope="col">Role</th></tr>
<tr><td>Ada</td><td>Owner</td></tr>
</table>