Tables
A bus timetable is a table: every cell only makes sense together with its row heading and its column heading.
Tables are for data
Use a table when information has rows and columns: a timetable, a price list, the results of a survey. In the late 1990s many sites used tables to build whole page layouts, with menus in one cell and content in another. That causes real problems. Screen readers announce every cell with its row and column, so a layout table sounds like a confusing spreadsheet, and table layouts do not adapt well to small screens. Today layout is done with CSS, and tables are kept for data.
Rows, cells and headers
A table is built row by row. <table> wraps everything, each <tr> (table row) is one row, and inside a row each <td> (table data) is one cell. Cells that label a row or a column use <th> (table header) instead, which browsers show in bold and centre by default. The number of cells in each row should match, so the columns line up.
<table>
<caption>Bread prices, October 2026</caption>
<thead>
<tr><th scope="col">Loaf</th><th scope="col">Weight</th><th scope="col">Price</th></tr>
</thead>
<tbody>
<tr><th scope="row">Sourdough</th><td>800 g</td><td>6.50</td></tr>
<tr><th scope="row">Rye</th><td>600 g</td><td>5.00</td></tr>
</tbody>
</table>Make the table explain itself
Three additions turn a grid of cells into a table that everyone can read. First, <caption>, placed right after the opening <table> tag, gives the table a title; screen readers announce it before the contents, so the listener knows what the table is about. Second, the scope attribute on each <th> says whether it labels a column (scope="col") or a row (scope="row"). With it, a screen reader can read Rye, Price, 5.00 instead of a bare 5.00. Third, <thead>, <tbody> and <tfoot> group the header rows, the body rows and any total rows. They make the structure clear, help CSS style each part, and let browsers repeat the header on every printed page of a long table.
Cells that span
A cell can stretch across several columns with colspan or several rows with rowspan. In a weekly timetable, a lunch break that covers all five weekdays can be one cell with colspan="5". When a cell spans, the row it sits in needs fewer cells: if a row normally has 6 cells and one of them has colspan="5", that row contains only 2 cells in the HTML. Use spanning when it reflects the data; very complex tables are hard for everyone to read, so splitting one big table into two simpler ones is often better.
Wide tables can be a problem on phones. A table cannot wrap the way text does, so a table with many columns may be wider than the screen. A common fix is to wrap the table in an element that scrolls sideways on its own, using CSS, so the rest of the page does not get wider too.
Keep the data in each cell as plain as you can: one value per cell, units in the header (Price in euros) rather than repeated in every cell, and no empty cells used as spacers. A table built this way is easier to read aloud, easier to sort, and pastes cleanly into a spreadsheet.
| table | Wraps a table of data |
| tr | Table row |
| td | Data cell |
| th | Header cell that labels a row or column |
| caption | Title of the table, first inside table |
| scope | Says whether a th labels a col or a row |
| colspan / rowspan | A cell that covers several columns or rows |
- When should you use a table?
- For data in rows and columns
- For the layout of a whole page
- For any list
- Which element is one row?
- Which element labels a column?
- th
- td
- caption
- Where does caption go?
- Right after the opening table tag
- At the end of the table
- Before the table
- What does scope="row" on a th mean?
- It labels the cells in its row
- It labels its column
- It spans the row
- A row normally has 6 cells. One cell has colspan="4". How many cells are written in that row?
- Which element groups the header rows?
- thead
- tbody
- tfoot
- Why are layout tables a problem?
- Screen readers read them as data, and they do not adapt to small screens
- They are not valid HTML
- They cannot hold images
- Which attribute lets a cell cover two rows?
- rowspan="2"
- colspan="2"
- scope="2"
- What does a screen reader announce first for a table with a caption?
- The caption
- The last row
- Nothing
- Tables are for data: table, tr, th, td.
- caption names the table; scope ties headers to cells; thead, tbody, tfoot group rows.
- colspan and rowspan merge cells; keep tables simple.