A complex table can use unique header IDs and a headers list on data cells when simple row and column scope cannot express the relationships.
HTML complex tables: connect each value to all applicable headers
Use it for a real task
A finance summary crosses quarter and measure. One number belongs to the quarter, the measure, and a row label. Explicit IDs allow the cell to name all three. Before choosing this structure, ask whether two simpler tables would make the data easier to maintain and scan; markup that merely satisfies a parser may still force readers through an awkward grid.
<table>
<caption>Receipt review totals</caption>
<thead>
<tr><th id="batch" rowspan="2">Batch</th><th id="quarter" colspan="2">Quarter 47</th></tr>
<tr><th id="approved">Approved</th><th id="held">Held</th></tr>
</thead>
<tbody>
<tr><th id="batch-r47" scope="row">R-47</th>
<td headers="batch batch-r47 quarter approved">73</td>
<td headers="batch batch-r47 quarter held">4</td></tr>
</tbody>
</table>What the markup guarantees
Every headers token must match a header cell ID in this table. The numbers here describe one batch only; a production report must calculate them from its actual records and state the unit of measure.
Cost and limits
The DOM grows with rows and columns. Explicit associations add authoring work and more ways to introduce broken IDs, so validate them and simplify the table when the data model permits.
Common Mistakes
- Do not point headers at a td or an ID in another table.
- Do not use merged cells only to make a layout look aligned.
- Provide a caption that names the data set.
Connected lessons
- HTML data tables: name row and column relationships explicitly
- HTML table spans: merge cells only when the data relationship requires it
- HTML data table versus layout container: preserve relationships, not columns
Continue with: Semantic output prompts: verify structure after generation.
