How do you make a data table accessible? What do <th scope>, <caption>, and headers/id do — and why never tables for layout?
Associating headers with cells so a screen reader can announce a cell in context.
A data table's accessibility comes from the header–cell relationships that let a screen reader announce, at any cell, which row and column it belongs to — the equivalent of a sighted user glancing up and left. Mark header cells as <th> (not <td>), and give each a scope: scope="col" for column headers, scope="row" for row headers. With scope set, when the user navigates to a data cell the screen reader reads the associated column (and row) header before the value — 'Revenue, Q2, $4M' — instead of a bare '$4M'. Add a <caption> as the table's accessible name/title so users know what the table is before entering it. Use <thead>/<tbody> to structure it. For COMPLEX tables with split or multi-level headers where simple scope is ambiguous, use the headers/id mechanism: give each <th> an id and each <td> a headers attribute listing the ids that describe it — explicit but verbose, so prefer restructuring into simpler tables when you can. Two anti-patterns: (1) using <table> for visual LAYOUT — it forces AT into a data-table reading mode and announces meaningless row/column counts; use CSS grid/flex for layout instead (or role="presentation" only as a last resort on a genuine layout table). (2) A 'table' built from <div>s with no semantics — if you must, apply role="table/row/columnheader/cell", but a real <table> is far less error-prone. Empty header cells and merged cells are the usual real-world pain points.
Rendering tabular data; associating headers with cells and keeping tables out of layout.
<table>
<caption>Quarterly revenue by region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">EMEA</th> <!-- row header -->
<td>$3M</td>
<td>$4M</td> <!-- announced as "EMEA, Q2, $4M" -->
</tr>
</tbody>
</table>
<!-- Never use <table> to lay out a page; use CSS grid/flex instead. -->