LessonLesson 8 of 34

Build an accessible data table

Identify data that belongs in a table; add a caption; associate simple row and column headers.

Beginner6 min readLearn · Practice · Review

Published by HTML Code Runner · AI-assisted material · Independent editorial review pending · Editorial policy

On this page

Learning objectives

Identify data that belongs in a table; add a caption; associate simple row and column headers.

Prerequisites

Understand nested elements and lists.

Understand the markup

A table represents relationships along rows and columns. Use it for a timetable, price comparison, or measured results. It is unsuitable for arranging an entire page: page layout is a CSS responsibility. If the information still makes sense as unrelated cards, a table may not be the best choice.

Start with table and provide a caption that names the dataset. A tr defines a row, th defines a header cell, and td defines a data cell. A cell's position establishes which row and column it belongs to. Keep each row's effective cell count consistent unless a deliberate span accounts for the difference.

Group header rows in thead and ordinary data rows in tbody. A tfoot can hold totals. These groups clarify structure and may support useful printing behavior; they do not automatically make every cell a header. For a simple table, scope="col" on top headers and scope="row" on side headers explicitly identify their direction.

Complex grouped headers may need scope="colgroup" or explicit headers attributes pointing to header IDs. Prefer simplifying the table before adding a complex association scheme. Horizontal overflow on a small screen is often more understandable than shrinking text until it is unreadable. Keep the actual table semantics when adding a scroll container.

Try a working example

<table>
  <caption>Workshop schedule</caption>
  <thead><tr><th scope="col">Day</th><th scope="col">Topic</th></tr></thead>
  <tbody><tr><th scope="row">Monday</th><td>Links</td></tr>
  <tr><th scope="row">Tuesday</th><td>Forms</td></tr></tbody>
</table>

Expected output

The caption identifies the schedule. Monday and Tuesday label rows; Day and Topic label columns.

Another example

<table>
<caption>Bookshop sales</caption>
<thead><tr><th scope="col">Month</th><th scope="col">Books sold</th></tr></thead>
<tbody><tr><th scope="row">October</th><td>84</td></tr></tbody>
<tfoot><tr><th scope="row">Total</th><td>84</td></tr></tfoot>
</table>

Common mistakes

A bold td remains a data cell. Do not leave a header empty when a meaningful label is available. The obsolete border attribute is not a substitute for CSS.

Accessibility and browser notes

Captions and header associations help a reader understand a cell without seeing its position. Check the table by navigating cell by cell with a screen reader.

Check your work

Before styling, write down what a reader needs to know about any single data cell. In the workshop example, Links needs both the Monday row label and Topic column label. If that relationship is not clear from the markup, revise the headers. Test a longer topic name and a narrow viewport so you can discover overflow before publishing real data.

Practice

Create a two-column table of two library branches and their opening times. Include a caption and column headers.

Review the solution
<table><caption>Library branch opening times</caption>
<thead><tr><th scope="col">Branch</th><th scope="col">Opens</th></tr></thead>
<tbody><tr><th scope="row">North</th><td>09:00</td></tr><tr><th scope="row">South</th><td>10:00</td></tr></tbody></table>

Related lesson

Organize a page with semantic landmarks

Specifications and further reading

WHATWG HTML Living Standard

When a simple header model is no longer enough

A table with grouped headers can become difficult to understand even when every header uses th. A cell may need a row label, a subgroup label, a year, and a unit. Before implementing explicit header associations, ask whether two smaller tables would communicate the comparison more clearly. Complexity in the dataset does not require complexity in one visual grid.

If you keep a complex table, assign unique IDs to the relevant header cells and use headers on each data cell to list the IDs that describe it. Test the resulting associations with the assistive technology and browser combinations your audience uses. The existence of headers strings alone does not prove the navigation experience is good.

Sorting a table needs additional interaction design. Put a real button in a sortable header, expose the active sort direction with aria-sort on the appropriate header, and reorder the actual rows in the DOM. A decorative arrow without an accessible state leaves some readers unable to determine the current order. Numeric and localized currency values may need machine-oriented sorting keys rather than lexical sorting of their displayed text.

One more skill in your toolkit.

Finished the explanation and exercise? Save your progress.