back to CanAdapt home

When the header row is in a separate table, use a ghost row

Every once in a while I come across a table where the header row must be in a separate table above the data table, because some particular functionality "requires it". Since many clients already have this in place, I've cooked up a little work around that seems to work fine in new screen readers. I created a ghost heading row in the data table positioning it offscreen, and placed aria-hidden="true" on the table element that contains their headings, so the screen reader ignores that one row table. Below is an example.

Ghost heading 1 Ghost heading 2 Ghost heading 3 Ghost heading 4
1 2 3 4
5 6 7 8
99 9 0 11



.hideme {
height: 1px; border: none }
.noborder{border: none}
.offscreen {
height: 1px; width: 1px; position: absolute; overflow: hidden; top: -10px;}
.header {font-weight:bold;


<table width="469" border="1" aria-hidden="true">
<td width="139" scope="col" class="header">Widget 1</td>
<td width="94" scope="col" class="header">Widget 2</td>
<td width="94" scope="col" class="header">Widget 3</td>
<td width="114" scope="col" class="header">Widget 4</td>
<table width="469" border="1">
<tr class="hideme">
<th width="139" class="noborder" scope="col" > <span class="offscreen">Ghost heading 1</span></th>
<th width="93" class="noborder" scope="col"><span class="offscreen" >Ghost heading 2</span></th>
<th width="94" class="noborder" scope="col"><span class="offscreen">Ghost heading 3</span></th>
<th width="115" class="noborder" scope="col"><span class="offscreen">Ghost heading 4</span></th>

The table code might be something like this:

Accessibility Support technology used

  JAWS NVDA VoiceOver
FireFox32 OK OK N/A
Chrome 37 OK OK N/A
Safari MacOS N/A N/A OK


This is fairly elegant and reads in all modern screen readers