Complex Tables
title: "Complex Tables — Making Multi-Header Tables Accessible" description: "Learn how to create accessible complex tables with multiple header rows and columns using scope, headers, and proper table structure." weight: 6 date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, tables]
Complex tables with multi-level headers require careful use of scope, headers, and id attributes to create correct cell-to-header associations.
## What You'll Learn
How to structure tables with multiple header rows and columns, and how to use headers attributes for correct associations.
## Why It Matters
Complex tables appear in financial reports, scientific data, and product comparisons. Incorrect header associations make these tables unusable for screen reader users.
## Real-World Use
A financial table has year and quarter headers in two rows, and department and sub-department headers in two columns. The headers attribute on each cell associates it with the correct year, quarter, department, and sub-department.
## Multi-Header Table
```html
<table>
<caption>Department budget by quarter</caption>
<tr>
<th id="blank" rowspan="2"></th>
<th id="dept" colspan="2">Engineering</th>
<th id="dept2" colspan="2">Marketing</th>
</tr>
<tr>
<th id="eng-salary" headers="dept">Salaries</th>
<th id="eng-op" headers="dept">Operations</th>
<th id="mkt-salary" headers="dept2">Salaries</th>
<th id="mkt-op" headers="dept2">Operations</th>
</tr>
<tr>
<th id="q1" scope="row">Q1</th>
<td headers="q1 dept eng-salary">$200K</td>
<td headers="q1 dept eng-op">$50K</td>
<td headers="q1 dept2 mkt-salary">$150K</td>
<td headers="q1 dept2 mkt-op">$40K</td>
</tr>
<tr>
<th id="q2" scope="row">Q2</th>
<td headers="q2 dept eng-salary">$210K</td>
<td headers="q2 dept eng-op">$55K</td>
<td headers="q2 dept2 mkt-salary">$160K</td>
<td headers="q2 dept2 mkt-op">$45K</td>
</tr>
</table>
Row Group Headers
<table>
<caption>Employee data by region</caption>
<tr>
<th id="region" rowspan="2">Region</th>
<th id="name" rowspan="2">Name</th>
<th id="role" colspan="2">Position</th>
</tr>
<tr>
<th id="title" headers="role">Title</th>
<th id="level" headers="role">Level</th>
</tr>
<tr>
<th id="west" scope="rowgroup" rowspan="2">West</th>
<th id="alice" scope="row">Alice</th>
<td headers="west alice title">Engineer</td>
<td headers="west alice level">Senior</td>
</tr>
</table>
Common Mistakes
1. Missing rowspan on row group headers
When a header spans multiple rows, use rowspan on the th element.
2. Inconsistent headers references
Every cell in the same column must reference the same set of header ids.
3. Circular headers references
Headers that reference each other create infinite loops. Ensure references are one-directional.
4. No scope="rowgroup" for grouped row headers
When a row header spans multiple rows, use scope="rowgroup".
5. Too many headers references
Keep header associations to 3-4 references per cell. Too many references confuse users.
Practice Questions
1. What attribute spans a header across multiple rows? rowspan on the th element extends the header across multiple rows.
2. How do you associate a cell with a header two levels up? Use the headers attribute with space-separated ids: headers="top-header mid-header current-header".
3. What scope value is used for a header that labels a group of rows? scope="rowgroup" indicates the header labels a group of rows.
Challenge: Create a complex table with two header rows and two header columns. Use headers attributes for all data cells.
FAQ
Mini Project
Create a complex table with three header rows (year, quarter, month) and two header columns (department, team). Use headers to create all associations.
What's Next
Learn how to provide a Summary Description for tables using aria-describedby.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro