Use real table markup for tabular data
Information is tabular when the same piece of data has a relationship in two directions at once, down its column and across its row. Where that is true the markup should be table, tr, th and td, because screen readers ship commands built specifically for those elements. W3C lists H51 as sufficient for 1.3.1 Info and Relationships. A grid of divs, a run of tab characters or a pre block can produce exactly the same picture and expose no rows, no columns and no headers underneath. The reverse also holds. A table that is only positioning content is not carrying data, and CSS is the better tool for that job, though WCAG stops short of banning the practice.
How we find it in an audit
Heuristics catch some div grids and miss plenty of others. Deciding that something is genuinely tabular is judgment. So we point a screen reader's table commands at anything that looks like a grid, pricing comparisons and schedules and specification sheets, and note every place those commands find nothing to work with.
How affected users experience it
In a real table a screen-reader user moves cell by cell, hears the row and column headers alongside each value, and can read one column top to bottom. In a div grid none of those commands do anything, and the whole thing arrives as a single long line. Basic 10 GB 5 users Pro 100 GB 25 users. A comparison table that cannot be compared.
Passes vs. fails
Passes
<table>
<tr><th scope="col">Plan</th><th scope="col">Storage</th><th scope="col">Seats</th></tr>
<tr><td>Pro</td><td>100 GB</td><td>25 users</td></tr>
</table>Fails
<div class="row">
<div>Pro</div><div>100 GB</div><div>25 users</div>
</div>How this gets tested
The W3C publishes test rules that define what a checker looks for here.
- ARIA attribute is defined in WAI-ARIAA tool can check this
- ARIA state or property has valid valueA tool can check this
- Element with role attribute has required states and propertiesA tool can check this
- Form field has non-empty accessible nameA tool can check this
- Headers attribute specified on a cell refers to cells in the same table elementA tool can check this
- Role attribute has valid valueA tool can check this
- ARIA global properties not used where prohibitedA tool can check this
- ARIA required context roleA tool can check this
- ARIA required owned elementsA tool can check this
- ARIA state or property is permittedA tool can check this
- Table header cell has assigned cellsA tool can check this
Other ways to satisfy this rule
52 guides on this site are filed under 1.3.1 Info and Relationships. W3C lists this one as sufficient for that rule when used for making information and relationships conveyed through presentation programmatically determinable, so the condition is part of the test rather than a footnote to it.
- ARIA11sufficientAdd landmarks so users can skip around
- ARIA12sufficientUse role=heading when h1-h6 is impossible
- ARIA13sufficientName repeated landmarks so they are distinguishable
- ARIA16sufficientName controls from visible text with aria-labelledby
- ARIA17sufficientGroup related form fields with ARIA roles
- ARIA20sufficientMark leftover page areas with role=region
This guide is our interpretation of W3C technique H51: Using table markup to present tabular information. W3C publishes its techniques as guidance rather than as the standard, and says so on every one of them. The success criterion is what conformance is measured against, and a technique is one documented way to meet it.