Accessible Tables

Since tables are used to organize large bits of data it’s very important to structure the table correctly. We need to use proper syntax for assistive technology such as screen or brail readers, which means we’ll use <td>, <th>, and scope as needed. We also need to style tables in such a way that the organization and relationships are obvious to users with no assistive technology. Generally, that means that the <th> and <td> elements are styled differently. These items combined will make an accessible table.

Basic Rules

  • <th> elements should never be empty.
  • Use colspan and rowspan to expand cells as needed
  • Be sure to wrap rows inside of <thead>, <tbody>, or <tfoot> unless using <col> and <colgroup>. You can use both.

Captions

Captions are not required, but they are very helpful, especially when the tables are very complicated. Captions are simple to use. It doesn’t matter where you place the caption because it’s position, top or bottom, is determined by CSS.

<table>
	<caption>My pets</caption>
	<!-- rows/columns here -->
</table>
My pets
Dogs Cats Turtles
Pugsley Sandi Yertle
Wednesday Peppi Gimpy
Felicity Smoak Speedy

To change the position of the caption you can change the CSS property caption-side to be either top or bottom.

caption {
	caption-side: top;
}

Summary

If you find that a simple caption is not enough, and you need a longer description, you can use aria-describedby or a figure.

Using aria-describedby:

<p id="table-description">This is a long description for the table that is both visual, and accessible to those with assistive technology.</p>
<table aria-describedby="table-description"></table>

Using figures:

<figure>
	<figcaption>
		<strong id="table-caption">Caption Title</strong><br>
		<span id="table-summary">This is a long description for the table that is both visual, and accessible to those with assistive technology.</span>
	</figcaption>
	<table aria-labeledby="table-caption" aria-describedby="table-summary"></table>
</figure>

Headers

Basic tables with simple headers

If the table has one row of headers across the top, or one column of headers on the left, then you only need to use the standard <th> element for those headers.

sample of a simple html table
<table class="table">
	<thead>
		<tr>
			<th>Dogs</th>
			<th>Cats</th>
			<th>Turtles</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Pugsley</td>
			<td>Sandi</td>
			<td>Yertle</td>
		</tr>
		<tr>
			<td>Wednesday</td>
			<td>Peppi</td>
			<td>Gimpy</td>
		</tr>
		<tr>
			<td></td>
			<td>Felicity Smoak</td>
			<td>Speedy</td>
		</tr>
	</tbody>
</table>

Tables with two headers

Now that we’re introducing multiple headers, we’ll need to start adding a scope to the <th> elements.

preview of html table with 2 headers
<table class="table text-white">
	<caption>Shirt Stock</caption>
	<thead>
		<tr>
			<td></td>
			<th scope="column">SM</th>
			<th scope="column">MD</th>
			<th scope="column">LG</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<th scope="row">Yellow Shirt</th>
			<td>1</td>
			<td>3</td>
			<td>2</td>
		</tr>
		<tr>
			<th scope="row">Black Shirt</th>
			<td>0</td>
			<td>6</td>
			<td>8</td>
		</tr>
		<tr>
			<th scope="row">Teal Shirt</th>
			<td>9</td>
			<td>9</td>
			<td>9</td>
		</tr>
	</tbody>
</table>

Multiple headers

This table is a little more complicated so we’re going to ditch the <thead> and <tbody> elements and use <col> and <colgroup> instead. We can still use both, but it isn’t necessary.

preview of accessible table with multiple headers
<table class="table text-white">
	<caption>Company Years Experience</caption>
	<col>
	<colgroup span="3"></colgroup>
	<colgroup span="3"></colgroup>
	<tr>
		<td rowspan="2"></td>
		<th colspan="3" scope="colgroup">Frontend</th>
		<th colspan="3" scope="colgroup">Backend</th>
	</tr>
	<tr>
		<th scope="col">HTML</th>
		<th scope="col">CSS</th>
		<th scope="col">JS</th>
		<th scope="col">PHP</th>
		<th scope="col">Python</th>
		<th scope="col">SQL</th>
	</tr>
	<tr>
		<th scope="row">Mathew</th>
		<td>1</td>
		<td>1</td>
		<td>1</td>
		<td>1</td>
		<td>1</td>
		<td>1</td>
	</tr>
	<tr>
		<th scope="row">Mark</th>
		<td>24</td>
		<td>15</td>
		<td>10</td>
		<td>9</td>
		<td>2</td>
		<td>15</td>
	</tr>
	<tr>
		<th scope="row">Luke</th>
		<td>20</td>
		<td>3</td>
		<td>20</td>
		<td>0</td>
		<td>0</td>
		<td>20</td>
	</tr>
</table>