Avoid Spanning Table Headers
Spanned table headers are not well supported across screen readers. While you can visually style these all sorts of ways to make the spanning clear, I am focusing on the programmatic outcomes. Which essentially means how they are exposed to screen reader users.
This post uses only HTML <table>
s. It does not consider ARIA table roles, partly because while ARIA has a method to span cells, there is no headers
equivalent and no scope
equivalent.
I made three table variations using the same structure, with the only difference in how or if it uses scope
. One uses the scope
attribute along with <colgroup>
, <thead>
, and <tbody>
elements to try to provide the maximum number of programmatic cues. The second uses none of those. The third one only uses a single scope
, and solely because without it the header cell could be understandably interpreted either way.
To manage expectations, I did not record any videos. Nor did I file any bugs. I simply wanted to get this documented (and maybe get feedback) and then I can try to trace the issues more specifically.
I recommend you test these yourself. I have linked resources for testing with screen readers in another post.
Testing Notes
In the lists of notes I represent column and row headers in italics using <em>
and regular data cells in quotes. These tests are not exhaustive and I did not document every detail.
My testing kit:
- Screen Readers
- NVDA 2022.4
- JAWS 2023
- Narrator on Windows 11 22H2
- VoiceOver on macOS 12.6.3
- Browsers
- Firefox 109
- Chrome 110
- Edge 110
- Safari 16.3
Obviously test with the kit your users use.
1. Uses scope
, <colgroup>
, <thead>
, <tbody>
You can visit the debug mode of this first table for easier testing.
See the Pen 1. Uses <code>scope</code>, <code><colgroup></code>, <code><thead></code>, <code><tbody></code> by Adrian Roselli (@aardrian) on CodePen.
- NVDA
- Firefox
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Chrome
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Firefox
- JAWS
- Chrome
- Announces Region and Author column header twice.
- Starts to navigate diagonally from “Cervantes” when going down or across.
- Skips spanned headers when navigating headers horizontally.
- Assigns wrong column and row headers.
- Firefox
- Gets the spanning headers wrong and sometimes navigates into the wrong cell.
- Thinks spanned column headers each have a row header of the non-spanning column headers to the left.
- JAWS silently quit more than once, inconsistently.
- Assigns wrong column and row headers.
- Navigates diagonally or unexpectedly.
- Chrome
- Narrator
- Edge
- Does not announce row headers.
- Navigates diagonally or unexpectedly.
- Cannot easily navigate to cells with spanning row headers, especially Nnedi Okorafor‘s books.
- Edge
- VoiceOver
- Safari/macOS
- Announces the ISBN and Formats headers as being in Asia‘s row.
- Announces the 13, 10, Pulp, etc. headers as being the Europe row.
- Only announces the spanning column header when moving into the first spanned column (not when navigating within the range of spanned columns, which is expected).
- When in a book in the Title column that is spanned, navigating left brings me to the Format column of the prior row.
- The same is true for an Author entry.
- The only way to enter the Africa cell is by moving down from North America or left from “The Book of Phoenix” (skipping author).
- Safari/macOS
Both Narrator and JAWS navigated diagonally (incorrectly), which is quite problematic. Otherwise, they all had some issues with header assignment, with JAWS and VoiceOver getting them wrong the most and Narrator not even announcing them.
The HTML for the first few rows:
<table>
<caption>Books 1</caption>
<colgroup>
<col>
<col>
<col>
<col>
<col span="2">
<col span="3">
</colgroup>
<thead>
<tr>
<th rowspan="2" scope="col">Region</th>
<th rowspan="2" scope="col">Author</th>
<th rowspan="2" scope="col">Title</th>
<th rowspan="2" scope="col">Year</th>
<th colspan="2" scope="colgroup">ISBN</th>
<th colspan="3" scope="colgroup">Formats Available</th>
</tr>
<tr>
<th scope="colgroup">13</th>
<th scope="colgroup">10</th>
<th scope="colgroup">Pulp</th>
<th scope="colgroup">Audio</th>
<th scope="colgroup">Digital</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Asia</th>
<th scope="row">Murasaki Shikibu<br>(<span lang="ja">紫 式部</span>, Lady Murasaki)</th>
<td>The Tale of Genji<br>(<span lang="ja">源氏物語</span>, Genji monogatari)</td>
<td>1021</td>
<td>9780142437148</td>
<td>014243714X</td>
<td>✔</td>
<td></td>
<td></td>
</tr>
<tr>
<th scope="rowgroup" rowspan="7">Europe</th>
<th scope="rowgroup" rowspan="3">Miguel De Cervantes</th>
<td>The Ingenious Gentleman Don Quixote of La Mancha</td>
<td>1605</td>
<td>9783125798502</td>
<td>3125798507</td>
<td>✔</td>
<td>✔</td>
<td>✔</td>
</tr>
…
</table>
2. Does NOT use scope
, <colgroup>
, <thead>
, <tbody>
You can visit the debug mode of this second table for easier testing.
See the Pen 2. Does NOT use scope, <colgroup>, <thead>, <tbody> by Adrian Roselli (@aardrian) on CodePen.
- NVDA
- Firefox
- Announces the ISBN, 13, 10, Formats, etc. column headers as being in the Author and Region row.
- Does not announce Author as the column header; thinks Asia is the column header instead of Region.
- Cannot get into spanned column headers.
- Assigns wrong column and row headers.
- Navigates diagonally or unexpectedly.
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Chrome
- Does not announce the two row headers for the first data row, Asia or Murasaki.
- It thinks Region and Asia are the column headers for column 1, and Author and Murasaki are the column headers for column 2.
- Cannot get into spanned column headers.
- Assigns wrong column and row headers.
- Navigates diagonally or unexpectedly.
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Firefox
- JAWS
- Chrome
- It thinks Region and Asia are the column headers for column 1, and Author and Murasaki are the column headers for column 2.
- Cannot get into spanned column headers.
- Assigns wrong column and row headers.
- Navigates diagonally or unexpectedly.
- Cannot get into some cells with table navigation commands.
- Firefox
- Sometimes navigates into the wrong cell.
- Cannot get into spanned column headers.
- Assigns wrong column and row headers.
- Navigates diagonally or unexpectedly.
- Chrome
- Narrator
- Edge
- Does not announce row headers.
- Navigates diagonally or unexpectedly.
- Cannot easily navigate to cells with spanning row headers, especially Nnedi Okorafor‘s books.
- Thinks Asia is a column header for Region, and Murasaki for Author.
- Edge
- VoiceOver
- Safari/macOS
- Does not announce the non-spanning column header that accompanies a spanning column header (so you do not know which Format or which ISBN).
- Announces neither country nor author when navigating down the non-country-non-author rows.
- Only announces the spanning column header when moving into the first spanned column (not when navigating within the range of spanned columns, which is expected).
- When in a book in the Title column that is spanned, navigating left brings me to the Format column of the prior row.
- The same is true for an Author entry.
- The only way to enter the Africa cell is by moving down from North America or left from “The Book of Phoenix” (skipping author).
- Safari/macOS
In addition to Narrator and JAWS navigated diagonally (incorrectly), NVDA has joined the chaos. Otherwise, they all had some issues with header assignment, with NVDA catching up to JAWS and VoiceOver and Narrator again not announcing them.
The HTML for the first few rows:
<table>
<caption>Books 2</caption>
<tr>
<th rowspan="2">Region</th>
<th rowspan="2">Author</th>
<th rowspan="2">Title</th>
<th rowspan="2">Year</th>
<th colspan="2">ISBN</th>
<th colspan="3">Formats Available</th>
</tr>
<tr>
<th>13</th>
<th>10</th>
<th>Pulp</th>
<th>Audio</th>
<th>Digital</th>
</tr>
<tr>
<th>Asia</th>
<th>Murasaki Shikibu<br>(<span lang="ja">紫 式部</span>, Lady Murasaki)</th>
<td>The Tale of Genji<br>(<span lang="ja">源氏物語</span>, Genji monogatari)</td>
<td>1021</td>
<td>9780142437148</td>
<td>014243714X</td>
<td>✔</td>
<td></td>
<td></td>
</tr>
<tr>
<th rowspan="7">Europe</th>
<th rowspan="3">Miguel De Cervantes</th>
<td>The Ingenious Gentleman Don Quixote of La Mancha</td>
<td>1605</td>
<td>9783125798502</td>
<td>3125798507</td>
<td>✔</td>
<td>✔</td>
<td>✔</td>
</tr>
…
</table>
3. Uses Single scope
; Does NOT Use <colgroup>
, <thead>
, <tbody>
Added scope="col"
to the Author and Region header cells and scope="row"
to the Asia and Murasaki header cells.
You can visit the debug mode of this third table for easier testing.
See the Pen 3. Uses a Single scope; Does NOT Use <colgroup>, <thead>, <tbody> by Adrian Roselli (@aardrian) on CodePen.
- NVDA
- Firefox
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Chrome
- When Entering the Nnedi Okorafor cell, it always announces its row header as North America and you cannot move within it to make it announce Africa.
- Firefox
- JAWS
- Chrome
- Announces Region and Author column header twice.
- Starts to navigate diagonally from “Cervantes” when going down or across.
- Skips spanned headers when navigating headers horizontally.
- Assigns wrong column and row headers.
- Firefox
- Gets the spanning headers wrong and sometimes navigates into the wrong cell.
- Chrome
- Narrator
- Edge
- Does not announce row headers.
- Navigates diagonally or unexpectedly.
- Cannot easily navigate to cells with spanning row headers, especially Nnedi Okorafor‘s books.
- Edge
- VoiceOver
- Safari/macOS
- Announces the ISBN and Formats headers as being in Asia‘s row.
- Announces the 13, 10, Pulp, etc. headers as being the Europe row.
- Only announces the spanning column header when moving into the first spanned column (not when navigating within the range of spanned columns, which is expected).
- When in a book in the Title column that is spanned, navigating left brings me to the Format column of the prior row.
- The same is true for an Author entry.
- The only way to enter the Africa cell is by moving down from North America or left from “The Book of Phoenix” (skipping author).
- Announces neither country nor author when navigating down the non-country-non-author rows.
- Safari/macOS
The minimal scope
addition cleans up NVDA and reduces errors for JAWS and VoiceOver, but bugs persist. Narrator continues to Narrator.
The HTML for the first few rows:
<table>
<caption>Books 3</caption>
<tr>
<th rowspan="2" scope="col">Region</th>
<th rowspan="2" scope="col">Author</th>
<th rowspan="2">Title</th>
<th rowspan="2">Year</th>
<th colspan="2">ISBN</th>
<th colspan="3">Formats Available</th>
</tr>
<tr>
<th>13</th>
<th>10</th>
<th>Pulp</th>
<th>Audio</th>
<th>Digital</th>
</tr>
<tr>
<th scope="row">Asia</th>
<th scope="row">Murasaki Shikibu<br>(<span lang="ja">紫 式部</span>, Lady Murasaki)</th>
<td>The Tale of Genji<br>(<span lang="ja">源氏物語</span>, Genji monogatari)</td>
<td>1021</td>
<td>9780142437148</td>
<td>014243714X</td>
<td>✔</td>
<td></td>
<td></td>
</tr>
<tr>
<th rowspan="7">Europe</th>
<th rowspan="3">Miguel De Cervantes</th>
<td>The Ingenious Gentleman Don Quixote of La Mancha</td>
<td>1605</td>
<td>9783125798502</td>
<td>3125798507</td>
<td>✔</td>
<td>✔</td>
<td>✔</td>
</tr>
…
</table>
Verdict
Unless testing with screen readers demonstrates otherwise, you can omit scope
, even on spanning column and row headers, along with <colgroup>
, <thead>
, and <tbody>
. However, you will want avoid spanning cells in general given table navigation and header announcement bugs across browsers and screen readers.
We already know that the scope
attribute is unnecessary to convey column and row headers for simple tables. Steve Faulkner documented this in late 2018 with Tables and Beers and again a few days later in Tables, Tequila and Beer, and then wrapped those up just a few more days later in Short note on scoping mechanisms.
My tests were meant to see if scope
would be useful on tables with spanning headers, specifically using the values colgroup
and rowgroup
. Mostly no, and definitely not with those two values. In the first row where the browser is not sure if the first cell of the table is a row header or column header, using scope="col"
proved useful. But it took some testing to confirm that.
Of course all of this, and the buggy behavior I outlined, is mooted if you avoid spanning cells to begin with.
Related
- Brief Note on Calendar Tables, 9 August 2022, touches on
abbr
attribute support. - Column Headers and Browser Support, 20 February 2022, touches on placement of column headers for best support.
- Accessible Cart Tables?, 21 January 2022, touches on
headers
attribute support.
7 Comments
I’ve always relied on using the
header
attribute with the associatedid
when creating complex tables.Could you just this method?
Tables with Multi-Level Headers | Web Accessibility Initiative (WAI) | W3C
In response to .Doesn’t work. Unless you use “Example 3” from the W3C tutorial (which splits the table into two tables, removing the need for a
headers
attribute). Consider the tutorial recommends the deprecatedsummary
attribute too.See my third bullet under Related above.
Of course all of this, and the buggy behavior I outlined, is mooted if you avoid spanning cells to begin with.
I would have liked to see that conclusion proven. Now I’m left to imagine what the spanless example looks like and wonder how much repetition would be required to convey the same detail.
In response to .I imagine that it might be different for different tables. For my fake table, I imagine you could dump the spanned row headers. For example, the region and author can be repeated in each row. The ISBN column headers I imagine need not have the word “ISBN” split up (it can be in each cell), and “Format” may not be necessary.
Alternatively, I imagine for other kinds of data you could explore splitting the tables and using headings for the kinds of column/row headers that span lots of cells. I imagine table captions can provide further clarification. I also imagine a designer and/or information architect could evaluate if all the data points are even necessary.
Grab some users and test how assorted methods compare for the data on hand and their expectations, then there will be little need to wonder how much repetition is needed.
I’m wondering if the issue with North America and Africa is because the
rowspan="4"
for North America when there are only three books by North American authors unless the first book by Nnedi Okorafor was written in North America.The Africa
rowspan="2"
when it might need to be three.I could be wrong about that.
In response to .Yup, Nnedi Okorafor intentionally spans both North America and Africa. I don’t know where the books were actually written, I just wanted to span a row header across two preceding row headers and that seemed like a fit.
Okay, I was sure if it was by design or not.
Good luck getting things to work as expected.
Leave a Comment or Response