TaskBuild a quarterly revenue table. <caption>Quarterly revenue, 2025</caption>, <thead><tr><th scope='col'>Quarter</th><th scope='col'>Revenue</th></tr></thead>, <tbody> with two rows: Q1/$120K and Q2/$180K. Each Quarter cell is a <th scope='row'>.
Tables: <caption>, <thead>, <tbody>, <th scope>
100 XP8 min
Theory
Tables are STILL the right tool for tabular data
For 20 years we've been told "tables are for data only, never layout." Both halves are true: don't use <table> to lay out a page. But when you DO have tabular data β a price chart, a leaderboard, an invoice β a <table> is correct and irreplaceable. Screen readers have keyboard shortcuts to navigate cells by row/column; CSS grid does not.
<table>
<caption>Quarterly revenue, 2025</caption>
<thead>
<tr>
<th scope="col">Quarter</th>
<th scope="col">Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Q1</th>
<td>$120K</td>
</tr>
<tr>
<th scope="row">Q2</th>
<td>$180K</td>
</tr>
</tbody>
</table>What each tag does
- `<caption>` β the table's title (rendered above by default). Screen readers announce it before the table content. Always include it; without one the user has no idea what the table is about.
- `<thead>` / `<tbody>` β semantic grouping. Screen readers can "skip to data" past the header row.
- `<th scope="col">` β this cell is a header for the column below it. Screen readers will associate every cell in that column with this header.
- `<th scope="row">` β this cell is a header for the row to its right. Often the leftmost cell of each body row.
- `<td>` β regular data cell.
Without scope, screen readers guess
A blind user reading a price chart cell-by-cell hears "$120K" with no context. With scope="col" and scope="row" properly set, they hear "Q1, Revenue, $120K." That's the difference.
π
Sign up to start coding
Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β card required, cancel anytime.
Sign up β free trial βFirst 10 lessons in each track are free. No card needed for those.