Tables

Tables Overview

Tables are primarily used to present tabular information, however, tables are also still used for creating layouts. While WCAG does not prohibit the use of tables for layouts, it encourages developers to use CSS instead.

Tables used to display tabular information can be divided into two groups – simple layouts and complex layouts. Simple layouts have a single row of headers at the top of the table and/or a single column of headers on the left side of the table. Additionally, there are no merged cells within the table. Complex data tables have either multiple rows or columns of headers, contain merged cells, or contain blank rows or columns for visual effect.

Additionally, data tables must also have a caption or a summary.

Caption

The caption element identifies the table. It can be thought of as a title or a heading for a table.

<table>
  <caption>Schedule for the week of March 6</caption>
  <tr>
    <th scope="col">State & First</th>
    <th scope="col">State & Sixth</th>
    <th scope="col">State & Fifteenth</th>
    <th scope="col">Fifteenth & Morrison</th>
  </tr>
  <tr>
    <td>4:00</td>
    <td>4:05</td>
    <td>4:11</td>
    <td>4:19</td>
  </tr>
  …

</table>

Summary

The summary can perform two different functions. It is allowable to have the summary perform both functions at the same time.  Support for the summary attribute varies, so if a table is complex enough to warrant a summary, it is best to try and simplify the table and add if necessary add a written description near the table on the same webpage.

  • it can describe the layout of the table to help screen reader users know how to navigate the table
  • it can describe in narrative form what information the table is trying to convey
<table summary="Schedule for Route 7 going downtown. Service begins 
at 4:00 AM and ends at midnight. Intersections are listed in the top row. 
Find the intersection closest to your starting point or destination, then read 
down that column to find out what time the bus leaves that intersection.">
  <tr>
    <th scope="col">State & First</th>
    <th scope="col">State & Sixth</th>
    <th scope="col">State & Fifteenth</th>
    <th scope="col">Fifteenth & Morrison</th>
  </tr>
  <tr>
    <td>4:00</td>
    <td>4:05</td>
    <td>4:11</td>
    <td>4:19</td>
  </tr>
  …
</table>

A simple data table with column and row headings

Source Code

<table border="1" summary="Pizza sizes are listed in the first row. Types of pizza are listed in the first column. Cross-reference them to determine the price of the pizza.">
  <caption>Pizza Prices</caption>
  <tr>
    <th scope="col">&nbsp;</th>
    <th scope="col">Small</th>
    <th scope="col">Medum</th>
    <th scope="col">Large</th>
  </tr>
  <tr>
    <th scope="row">Cheese</th>
    <td>$8</td>
    <td>$10</td>
    <td>$12</td>
  </tr>
  <tr>
    <th scope="row">Veggie</th>
    <td>$10</td>
    <td>$12</td>
    <td>$14</td>
  </tr>
  <tr>
    <th scope="row">Pepperoni</th>
    <td>$10</td>
    <td>$12</td>
    <td>$14</td>
  </tr>
</table>

Implementation

Small Medum Large
Cheese $8 $10 $12
Veggie $10 $12 $14
Pepperoni $10 $12 $14

A complex data table using header and id attributes

Source Code

<table border="1" summary="Pizza toppings are listed in the first two rows. Sizes of pizza are listed in the first column. Cross reference them to determine the price of each topping.">
  <caption>Topping Prices</caption>
  <tr>
    <th scope="col">&nbsp;</th>
    <th colspan="3" scope="col" id="meat">Meat Toppings</th>
    <th colspan="3" scope="col" id="veg">Vegetable Toppings</th>
  </tr>
  <tr>
    <th scope="col">&nbsp;</th>
    <th scope="col" id="pep">Pepperoni</th>
    <th scope="col" id="sa">Sausage</th>
    <th scope="col" id="bac">Bacon</th>
    <th scope="col" id="on">Onion</th>
    <th scope="col" id="ol">Olives</th>
    <th scope="col" id="tom">Tomato</th>
  </tr>
  <tr>
    <th scope="row" id="sm">Small</th>
    <td headers="meat pep sm">$0.80</td>
    <td headers="meat sa sm">$0.80</td>
    <td headers="meat bac sm">$1.00</td>
    <td headers="veg on sm">$0.60</td>
    <td headers="veg ol sm">$0.80</td>
    <td headers="veg tom sm">$0.60</td>
  </tr>
  <tr>
    <th scope="row" id="med">Medium</th>
    <td headers="meat pep med">$1.00</td>
    <td headers="meat sa med">$1.00</td>
    <td headers="meat bac med">$1.20</td>
    <td headers="veg on med">$0.80</td>
    <td headers="veg ol med">$1.00</td>
    <td headers="veg tom med">$0.80</td>
  </tr>
  <tr>
    <th scope="row" id="lg">Large</th>
    <td headers="meat pep lg">$1.20</td>
    <td headers="meat sa lg">$1.20</td>
    <td headers="meat bac lg">$1.40</td>
    <td headers="veg on lg">$1.00</td>
    <td headers="veg ol lg">$1.20</td>
    <td headers="veg tom lg">$1.00</td>
  </tr>
</table>

Implementation

Meat Toppings Vegetable Toppings
Pepperoni Sausage Bacon Onion Olives Tomato
Small $0.80 $0.80 $1.00 $0.60 $0.80 $0.60
Medium $1.00 $1.00 $1.20 $0.80 $1.00 $0.80
Large $1.20 $1.20 $1.40 $1.00 $1.20 $1.00
Further Reading