Tables
Tables present data in rows and columns: timetables, price lists, sports results, comparison charts. HTML tables have a clear structure of rows, header cells and data cells — and using them correctly lets screen readers announce which header each value belongs to.
Basic table structure
<table>wraps the whole table.<tr>("table row") holds one row.<th>("table header") is a heading cell — bold and centred by default.<td>("table data") is an ordinary cell.
Every row should have the same number of cells (unless cells span several columns — next lesson).
Adding borders and spacing with CSS
Unstyled tables have no borders. A few lines of CSS make them readable:
border-collapse: collapse merges neighbouring borders into single lines. Alternating row colours ("zebra stripes") help eyes follow long rows.
Row headers and scope
Headers aren't only along the top. When the first column labels each row, make those cells <th> too, and use scope to say which direction each header applies to:
A screen reader on the "1 TB" cell can now announce "Pro, Storage, 1 TB". scope="col" means the header labels its column; scope="row" labels its row.
Captions
<caption> must be the first thing inside the table. It acts as the table's title and is announced by screen readers — the equivalent of alt text for a table.
Tables are for data, not layout
In the early web, tables were used to arrange whole page layouts into columns. That makes pages hard to use with screen readers, hard to make responsive, and hard to maintain. Use CSS (Flexbox and Grid, lesson 25) for layout, and tables only for genuinely tabular data — information you'd put in a spreadsheet.
Wide tables on small screens
Tables with many columns don't fit on phones. The simplest robust fix is to let the table scroll sideways inside a wrapper:
Labelling the <section> with aria-label makes it a named region for screen readers, and tabindex="0" lets keyboard users focus it and scroll with the arrow keys.
Try it yourself
Add a row for Sunday, and a caption: