Skip to content

Tables

Tabular data SHOULD be represented in a <table>.

First and foremost, tables should never be used for layout purposes. Only use tables for tabular data (information with meaning dependent on it being in columns or rows in a grid). When proper HTML markup is used to create a table, readers can navigate through a table one cell at a time and hear the column and row headers spoken to them.

Don't create fake tables.

Because tables display poorly on mobile devices, developers have often created "fake tables." Using many <div> elements to create columns and rows gave the visual appearance of a table without using <table> markup. Don't do this. This method breaks the ability of screen readers to communicate the semantic meaning and structure to blind users.


The caption element

Using the caption element as a child of the table element provides information about what that table contains. It must be the first thing in the table tag, as screen readers read the caption or name of the table when users navigate to the table, giving users a sense of what the table is about.

  • Data tables SHOULD have a programmatically-associated caption or name.

  • The name/caption SHOULD describe the identity of the purpose of the table accurately, meaningfully, and concisely.

  • The name/caption of each data table SHOULD be unique within the context of other tables on the same page.

<table>
  <caption>Arti's Class Schedule</caption>
  ...

You could also use the aria-label attribute, but remember, this would be invisible to sighted users.

<table aria-label="Arti's Class Schedule">

or, use aria-labelledby

<h3 id="tbl_caption">Arti's Class Schedule</h3>
<table aria-labelledby="tbl_caption">

DON'T use the first row to make a "fake" caption

<table>
  <tr>
    <th colspan="3">Arti's Class Schedule</th>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <th scope="col">Monday</th>
    <th scope="col">Tuesday</th>

Use Correct Semantics

  • Table headers MUST be designated with <th>.
  • Data table header text MUST accurately describe the category of the corresponding data cells.

Simple Tables

Tables that only have one header (column or row) are relatively simple for screen readers. The data in the table is descriptive on its own. No additional markup is necessary to make this table accessible.

Table with one column header

Semester Grades
Course Final Grade
Website Development with HTML5 A
Intro to Programming with JavaScript AB
<table>
  <caption>Semester Grades:</caption>
  <tr>
    <th>Course</th>
    <th>Final Grade</th>
  </tr>
  <tr>
    <td>Website Development with HTML5</td>
    <td>A</td>
  </tr>
  <tr>
    <td>Intro to Programming with JavaScript</td>
    <td>AB</td>
  </tr>
</table>

Table with one row header

Semester Grades:
Course Website Development with HTML5 Intro to Programming with JavaScript
Final Grade A AB
<table>
  <caption>Semester Grades</caption>
  <tr>
    <th>Course</th>
    <td>Website Development with HTML5</td>
    <td>Intro to Programming with JavaScript</td>
  </tr>
  <tr>
    <th>Final Grade</th>
    <td>A</td>
    <td>AB</td>
  </tr>
</table>

Complex Tables

In the above tables the data is self-evident. It's not likely you will think that the letter "A" is a course name. However, some tables may have ambiguous data.

The scope attribute

The scope attribute tells the browser and screen reader that everything under the column is related to the header at the top, and everything to the right of the row header is related to that header. It is placed in the th opening tag.

Tables with ambiguous data

The below table has city names for multiple columns. Using the scope attribute helps identify what column each city belongs to.

Birth City and Current Resident of Team
Last Name Birth Place Current City
Hoang Phoenix, AZ Flint, MI
Smith San Diego, CA Madion, WI
<table>
  <caption>Birth City and Current Resident of Team</caption>
  <tr>
    <th scope="col">Last Name</th>
    <th scope="col">Birth Place</th>
    <th scope="col">Current City</th>
  </tr>
  <tr>
    <td>Hoang</td>
    <td>Phoenix, AZ</td>
    <td>Flint, MI</td>
  </tr>
  <tr>
    <td>Smith</td>
    <td>San Diego, CA</td>
    <td>Madion, WI</td>
  </tr>
</table>

Table with row and column headers

Using scope is especially useful to help screen readers understand content in tables that have column and row headings.

Delivery slots:
Monday Tuesday Wednesday Thursday Friday
09:00 - 11:00 Closed Open Open Closed Closed
11:00 - 13:00 Open Open Closed Closed Closed
<table>
  <caption>Delivery slots:</caption>
  <tr>
    <td></td>
    <th scope="col">Monday</th>
    <th scope="col">Tuesday</th>
    <th scope="col">Wednesday</th>
    <th scope="col">Thursday</th>
    <th scope="col">Friday</th>
  </tr>
  <tr>
    <th scope="row">09:00 - 11:00</th>
    <td>Closed</td>
    <td>Open</td>
    <td>Open</td>
    <td>Closed</td>
    <td>Closed</td>
  </tr>
  <tr>
    <th scope="row">11:00 - 13:00</th>
    <td>Open</td>
    <td>Open</td>
    <td>Closed</td>
    <td>Closed</td>
    <td>Closed</td>
  </tr>
</table>

Tables with multiple headers for each data cell

To clarify what the headers are for each cell, you can use the colgroup and rowgroup values of the scope attribute.

The below example uses colgroup for the Intro to Database and Website Development headings.

This table could be split into two tables (Intro to Database grades and Website Development grades). This is a better option than making complex headers in one table.

Grades for Spring
Intro to Database Website Development
Quiz Score Exam Score Quiz Score Exam Score
Jane Hansen 89 98 83 91
Bob Smith 77 81 89 85
<table>
  <caption>Grades for Spring</caption>
  <tr>
    <td rowspan="2"></td>
    <th colspan="2" scope="colgroup">Intro to Database</th>
    <th colspan="2" scope="colgroup">Website Development</th>
  </tr>
  <tr>
    <th scope="col">Quiz Score</th>
    <th scope="col">Exam Score</th>
    <th scope="col">Quiz Score</th>
    <th scope="col">Exam Score</th>
  </tr>
  <tr>
    <th scope="row">Jane Hansen</th>
    <td>89</td>
    <td>98</td>
    <td>83</td>
    <td>91</td>
  </tr>
  <tr>
    <th scope="row">Bob Smith</th>
    <td>77</td>
    <td>81</td>
    <td>89</td>
    <td>85</td>
  </tr>
</table>

thead, tfoot, and tbody elements

These elements define table headers, footers, and body content. They provide no additional accessibility functionality. However, there is no harm in using them for table styling with CSS. The tbody element is always added in every table, even if you don't specify it in your code. That being said, it is best to add it to your code as it can provide more control over your table structure and styling.

Tuition and fees
Item In-state Out-of-state
Tuition $8,844 $17,787
Student service fees $2,028 $2,028
COB graduate level course fee $1,485 $1,485
Estimated totals $12,357 $21,300
<table>
  <caption>Tuition and fees</caption>

  <thead>
    <tr>
      <th scope="col">Item</th>
      <th scope="col">In-state</th>
      <th scope="col">Out-of-state</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Tuition</td>
      <td>$8,844</td>
      <td>$17,787</td>
    </tr>
    <tr>
      <td>Student service fees</td>
      <td>$2,028</td>
      <td>$2,028</td>
    </tr>
    <tr>
      <td>COB graduate level course fee</td>
      <td>$1,485</td>
      <td>$1,485</td>
    </tr>
  </tbody>

  <tfoot>
    <tr>
      <td>Estimated totals</td>
      <td>$12,357</td>
      <td>$21,300</td>
    </tr>
  </tfoot>
</table>

Best Practices

  • Let the window determine the width of the table to reduce horizontal scrolling required with low vision.

  • Use relative values (percentages) to define cell widths

  • Avoid using cell heights so the cell can naturally expand to expose it's content. This is useful for uses that increase the default size of screen text.

  • Avoid using <br> inside cells. This may cause data to no longer align correctly when the text is resized.

  • Align text data to the left and numeric data to the right so that people using larger text sizes or small screens can find the data easier to read.

  • Apply alternate color backgrounds as styling to tables for a visual guide. Also, highlighting the cell on mouseover and keyboard focus helps orient people to where they are.

  • Make sure there is appropriate contrast between the background color and text.

  • Make sure to separate each piece of data into individual rows. Placing all the data in one cell makes it impossible for screen readers to determine the relationship between data across columns (see below).

Incorrect way to display data

Annual Tour Dates
Month Date & Location
January 15th: Milwaukee, WI; 21st: Chicago, IL
February 6th: New York, NY; 11th: Richmond, VA

Correct way to display data

Annual Tour Dates
Month Date Location
January 15th Milwaukee, WI
21st Chicago, IL
February 6th New York, NY
11th Richmond, VA