Lesson 7 of 32
Tables
Marking up real tabular data correctly with table, thead, tbody, th and scope — and why using tables for page layout is a historical anti-pattern to avoid.
Tables have one, specific job: displaying real tabular data — rows and columns of related values, the kind of thing you'd genuinely put in a spreadsheet. This lesson covers marking that up correctly, and also addresses a piece of web history worth knowing about even though you shouldn't repeat it.
The basic structure
<table>
<caption>Course Pricing</caption>
<thead>
<tr>
<th scope="col">Plan</th>
<th scope="col">Price</th>
<th scope="col">Support</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Basic</th>
<td>$9/mo</td>
<td>Email</td>
</tr>
<tr>
<th scope="row">Pro</th>
<td>$29/mo</td>
<td>Priority</td>
</tr>
</tbody>
</table>Breaking that down:
<table>wraps the whole thing.<caption>is the table's title — announced by screen readers before the data, and shown visually above the table by default. It should be the first child inside<table>.<thead>groups the header row(s).<tbody>groups the actual data rows. Every table needs one, even a small table, once you're being deliberate about structure.<tfoot>(not shown above) groups a footer row — useful for a totals row in a table of numbers. It can appear either before or after<tbody>in the source; browsers still render it last.<tr>is one table row.<th>is a header cell — bold and centred by default.<td>is a regular data cell.
scope — why it matters for screen readers
A sighted reader scanning a table visually connects a cell to its column header just by looking up, and to its row header by looking left. A screen reader user, moving cell by cell, has no equivalent unless the table tells it explicitly which header applies.
<th scope="col">Price</th>
<th scope="row">Basic</th>scope="col" says: this header describes every cell below it, down its
column. scope="row" says: this header describes every cell to its right,
along its row. With scope set correctly, a screen reader can announce
"Price: $9/mo" or "Basic: Price, $9/mo" as it moves through cells, instead of
just reading raw values with no context. Without it, the reader hears a
sequence of numbers and words with no indication of what any of them mean.
For simple tables, adding scope costs almost nothing and is the single
biggest accessibility improvement you can make to a table.
Real tabular data vs the layout anti-pattern
Use <table> when the content genuinely is a grid of related data — a
pricing comparison, a class schedule, sports statistics, a spreadsheet-style
report. That's what the element, and every screen reader's table-reading
behaviour, is built around.
Do not use <table> to arrange unrelated content into a visual grid —
positioning a logo in one cell, navigation in another, and a sidebar in a
third, purely because a grid of <td> cells happens to produce that visual
layout. This was an extremely common technique in the late 1990s and early
2000s, before CSS layout tools were reliable, and it's worth knowing about
specifically so you can recognise and avoid it:
- It's inaccessible. A screen reader has no way to know a table is "just for layout" — it announces it as tabular data regardless, reading out row and column relationships that don't actually exist and confusing the user about content that has nothing to do with a table.
- It's inflexible. Reordering or reflowing a table-based layout for a different screen size is far harder than doing the same with tools actually designed for layout.
The real tools for visual layout are flexbox and CSS grid, covered in detail later in this course — they were built specifically to solve the layout problem that tables were only ever a workaround for.
Try it yourself
What to remember
<table>is for genuinely tabular data — not for visually arranging unrelated page content.<thead>,<tbody>and<tfoot>group rows by role;<caption>titles the whole table.<th scope="col">and<th scope="row">tell assistive technology which header applies to which cells — a small addition with a large accessibility payoff.- Using tables for page layout is a historical anti-pattern: inaccessible and inflexible. Flexbox and grid are the real layout tools, covered later.
Check yourself
3 questions · pass 3/3 to unlock Forms and Inputs
1.When is
<table>the right element to reach for?2.What does
scope="col"on a<th>actually do?3.Why was using tables for full page layout considered bad practice?
3 left to answer