How to Build Accessible Data Tables
Tables are a good way to present comparisons such as pricing, schedules, and specifications. Done properly, they're easy to scan and navigate with a screen reader. Done badly, with merged cells, missing headers, or visual tricks, they turn into a jumble.
Use tables for data, not layout
Tables are for information with rows and columns. Don't use them to arrange page layout. Use CSS for layout. A layout table forces screen readers to read the cells in an order that may not make sense.
Mark up headers
Tell the browser which cells are headers:
- Use
thfor header cells, andtdfor data cells - Add
scope="col"to column headers andscope="row"to row headers - Put column headers in a
theadand data in atbody
This lets a screen reader announce the relevant headers as users move through the table, so each value is understood in context, for example "Standard plan, price, 29 dollars."
Add a caption
A caption element gives the table a title that assistive technology announces. It also helps all users understand what the table shows. Put it first inside the table element.
<table>
<caption>Monthly plans and what's included</caption>
<thead>
<tr><th scope="col">Plan</th><th scope="col">Price</th></tr>
</thead>
<tbody>
<tr><th scope="row">Basic</th><td>$19</td></tr>
</tbody>
</table>
Keep structure simple
- Avoid merged cells (
colspanandrowspan) unless you truly need them, and if you do, use more advanced header associations carefully - Don't leave header cells empty. Give the top-left cell a label if it's meaningful
- Avoid nesting tables inside tables
- Keep tables small. If a table is huge, consider splitting it into several focused ones
Make content understandable
- Use clear header text
- Don't use color alone to convey meaning. Add text or icons. See color blindness and web design
- Align numbers consistently so they can be compared
- Use plain text in cells, avoiding unnecessary formatting
- Add units in headers or cells
Think about small screens
Wide tables can overflow on phones. Common solutions:
- Let the table scroll horizontally inside a container, and make the container keyboard accessible
- Reflow rows into stacked cards on narrow screens, while preserving the relationships between headers and values
- Show only the most important columns by default
Never hide headers visually in a way that removes them for screen readers. See responsive design basics.
Responsive tables and pricing
For pricing comparisons, consider whether a table or cards communicate better. See pricing page best practices.
Check contrast and readability
Zebra stripes and borders should keep text readable. See color contrast and readable text.
Test it
Tab through the page and use a screen reader to navigate the table cell by cell. Verify that it announces headers and the caption. See how to test with a screen reader.
Don't use images of tables
A screenshot of a table can't be read, searched, or resized. Use a real table. See alt text best practices.
The bottom line
Use real table markup with captions and header cells, keep structures simple, and plan for small screens. Then everyone can read your data.