Learn
02.1 | TABLES FOR TABULAR DATA
01 | WHEN A TABLE IS THE RIGHT TOOL
Two robotics teams have completed a skills challenge. You could describe every score in a paragraph, but comparing them would take effort. A table puts the same kinds of information in consistent columns.
| Team | Driver points | Autonomous points |
|---|---|---|
| Falcons | 42 | 18 |
| Comets | 38 | 24 |
Tabular data has meaningful relationships between rows and columns. Each row here describes one team. The columns describe its name and two scores. You can quickly see that Falcons scored more driver points, while Comets scored more autonomous points.
Use tables for results, timetables, prices or other related records. Use lists for a simple set of items. Use CSS layout tools for positioning page sections; a table should describe data rather than arrange a whole website.
02 | TABLE, ROW AND CELL
<table>
<tr>
<th>Team</th>
<th>Points</th>
</tr>
<tr>
<td>Falcons</td>
<td>60</td>
</tr>
</table>- table contains the table.
- tr creates a table row.
- th creates a header cell, describing a row or column.
- td creates a data cell.
Read the code a row at a time, from left to right. The first tr contains two headers. The second contains two values. HTML describes the structure; CSS controls borders, spacing and colours.
Put cells inside rows. A paragraph does not belong directly inside a tr. Text and suitable content can go inside a td or th.
03 | ADD A CAPTION AND GROUP THE ROWS
<table>
<caption>Robotics skills results</caption>
<thead>
<tr>
<th scope="col">Team</th>
<th scope="col">Driver points</th>
<th scope="col">Autonomous points</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Falcons</th>
<td>42</td>
<td>18</td>
</tr>
<tr>
<th scope="row">Comets</th>
<td>38</td>
<td>24</td>
</tr>
</tbody>
</table>A caption names the table and explains what the data represents. Put it immediately after the opening table tag in your beginner examples. It belongs to the table, so it is more meaningful than a separate paragraph that happens to sit nearby.
thead groups the header rows. tbody groups the main data rows. Both contain tr elements. They organize the structure but do not replace th and td.
A browser may create a tbody in its document tree when you omit it. Writing it explicitly makes your intended grouping clear. The table does not need a visible border to be a table.
04 | HEADERS GIVE THE VALUES CONTEXT
The number 24 means little on its own. In the results table it is Comets’ autonomous points. Row and column headers provide that context.
<th scope="col">Autonomous points</th>
<th scope="row">Comets</th>For a simple table, scope="col" identifies a header for its column, and scope="row" identifies a header for its row. These explicit relationships help assistive technology associate values with their headings.
Use th because a cell is a heading, not because you want bold text. Making a td bold with CSS does not give it header semantics. Similarly, not every first-column cell must be a th: choose it when it really labels the row.
For much more complex tables, explicit id and headers relationships may be needed. Prefer a simpler design when it communicates the same information clearly.
05 | TOTALS AND FOOTER ROWS
<tfoot>
<tr>
<th scope="row">Combined points</th>
<td>80</td>
<td>42</td>
</tr>
</tfoot>Place this tfoot inside the table, after tbody. It groups summary rows. The values here are column totals: 42 + 38 = 80 driver points, and 18 + 24 = 42 autonomous points.
HTML does not calculate totals. These are values you have entered. Changing a score does not automatically update a total. Calculations need another tool or programming logic.
tfoot belongs to a table. It is different from footer, which provides closing information for a page or section.
06 | COLSPAN: ONE CELL, TWO COLUMNS
<table>
<caption>Saturday robotics workshop</caption>
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Room A</th>
<th scope="col">Room B</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">09:00</th>
<td colspan="2">Shared safety briefing</td>
</tr>
<tr>
<th scope="row">09:30</th>
<td>Build a chassis</td>
<td>Program sensors</td>
</tr>
</tbody>
</table>| Time | Room A | Room B |
|---|---|---|
| 09:00 | Shared safety briefing | |
| 09:30 | Build a chassis | Program sensors |
colspan="2" makes the briefing cell occupy two columns. The first data row has two cell elements but covers three column positions: one for Time, plus two for the briefing.
Do not add another td after the briefing to stand for Room B. That position is already occupied. The next row returns to three ordinary cells.
Use merged cells only when they express a real relationship. A separate note that the briefing is shared may be simpler if the table becomes hard to navigate.
07 | ROWSPAN: LOOK AT THE WHOLE GRID
<table>
<caption>Equipment assigned to teams</caption>
<thead>
<tr>
<th scope="col">Team</th>
<th scope="col">Equipment</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2">Falcons</td>
<td>Robot kit</td>
</tr>
<tr>
<td>Sensor kit</td>
</tr>
</tbody>
</table>rowspan="2" makes Falcons occupy its current row and the next row. The second tr contains only one new cell because the first column position is already occupied.
This compact example demonstrates the grid. For a straightforward accessible records table, repeating “Falcons” in two ordinary rows can be clearer and avoids a merged cell. Do not merge cells just to avoid repeating a label.
Check column positions, not just the number of cell tags. Spanning cells explain why correctly built rows may contain different numbers of tags.
08 | BUILD, CHECK AND IMPROVE
- Decide what one row represents, then name the columns.
- Add a meaningful caption and suitable th headers.
- Write each row in the same column order.
- Check values against the column headings.
- Count the occupied column positions, including any spans.
- Test the page at a narrow width. Keep all data available.
A wide table may need a horizontally scrolling wrapper. That is a CSS enhancement, which you will learn later. Avoid deleting important columns or shrinking text until it becomes hard to read.
A missing value is not necessarily zero. Use a clear label such as “Not recorded” where appropriate. If a dash means something specific in your dataset, explain it.
Common mistakes include using td for every heading, putting cells outside tr, adding an extra cell after colspan, and expecting HTML to calculate totals.