When to use a table?
Tables are for tabular data: information that only makes sense when you cross rows and columns (a schedule, a price comparison, a leaderboard). If you strip the lines away and the data turns to mush, it was a table.
⚠️ Classic trap: using tables to lay out the page design. That was a 2000s move; today CSS Grid and Flexbox are there for placing things on screen. The table is for data, not for layout.
The anatomy of a table
<table>
<caption>First quarter grades</caption>
<thead>
<tr>
<th scope="col">Student</th>
<th scope="col">Math</th>
<th scope="col">Language</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Ana</th>
<td>9</td>
<td>8</td>
</tr>
<tr>
<th scope="row">Luis</th>
<td>7</td>
<td>9</td>
</tr>
</tbody>
</table>
Piece by piece:
<table>: the container of the whole table.<caption>: the title of the table. It goes right after<table>and screen readers announce it before reading the data.<thead>: groups the header row (or rows).<tbody>: groups the data rows.<tr>(table row): a row.<th>(table header): a header cell. By default it is bold and centered.<td>(table data): a normal data cell.
scope: the attribute that makes the table accessible
The scope attribute on a <th> indicates what it applies to that header:
scope="col": heads a column (typical in the top row).scope="row": heads a row (the first cell of each row, like the student's name).
This is key for accessibility: when someone with a screen reader is
on the "9" cell, the reader can announce "Ana, Math: 9" because it knows that
"Ana" is the header of its row and "Math" the header of its column. Without
scope, it would only hear "9", with no context.
A well-marked-up table (with
caption,thead/tbodyandth scope) makes sense even when you hear it blind. Pass that test and you've nailed it. That is the goal.