Skip to main content
Website guides
Accessibilityeasy4 min

How to Add Proper Table Headers for Accessible Data Tables

A data table without header cells is just a stream of numbers to a screen reader. Here is how to mark up tables so the data makes sense to everyone.

Reviewed by Jacob Rhinehart for Strelva's co-founder; products and operations lead responsibilities.

Tables are how we present comparisons, pricing, schedules, and specifications. A sighted reader scans across rows and down columns effortlessly. A screen-reader user moves cell by cell, and without proper header markup, each cell is just a value with no context: "199" with no idea which plan or which month it belongs to. Header cells restore that context.

Layout tables versus data tables

This applies to genuine data tables. If a table is only used to position elements visually, it should not be a table at all. For real data, the fix is straightforward.

Step by step

  1. Identify data tables. Pricing tables, comparison grids, schedules, and spec sheets all qualify.
  2. Mark the column headers. Change the top row's cells from <td> to <th scope="col">.
  3. Mark the row headers. If the first column labels each row, change those cells to <th scope="row">.
  4. Group the sections. Wrap the header row in <thead> and the data rows in <tbody>.
  5. Add a caption. A <caption> gives the table a title that is announced up front.
  6. Test. With a screen reader, moving between cells should announce the relevant column and row headers alongside each value.

The result

Now "199" is announced as "Pro plan, monthly price, 199." The numbers carry their meaning with them, and the table is usable by everyone.

Frequently asked

What business owners ask next.

What does the scope attribute do?

scope tells screen readers whether a header cell labels a column (scope="col") or a row (scope="row"). That association is what lets the reader announce the right header alongside each data cell.

Does this apply to tables I only use for layout?

No. Tables used purely to position elements should not be data tables at all; modern CSS handles layout. Header markup is for genuine data tables: pricing, schedules, comparisons, and specs.

My CMS won't let me add th elements. What can I do?

Switch to the HTML or code view of the table and edit the markup directly. If the table is built from div elements rather than real table tags, either rebuild it as a proper table or add table ARIA roles to the divs.

Rather have the website handled?

Strelva scopes and builds the site, then keeps the agreed surfaces current under a managed plan.

Get a quote