HTML has the ability to make complex data tables—with a little extra care they can be completely accessible.
Tabular data
Tables are great for large datasets, similar to how spreadsheets like in Excel or Numbers work.
Rows & cells
Tables are composed of rows, that move vertically, stacking on top of each other. Each row is then broken down into cells, that move horizontally across the page.
Here’s an example of what the structure looks like:
Name | Period | Discovery | Size | |
---|---|---|---|---|
Length | Mass | |||
Stegosaurus | Late Jurassic | 1877 | 9 m | 4.5 t |
Apatosaurus | Jurassic Period | 1877 | 23 m | 35 t |
Tyrannosaurus | Late Cretaceous | 1905 | 12.3 m | 6.8 t |
Total | 3 dinosaurs |
In HTML you can start a table with the <table>
element. Each row is represented by the <tr>
element. And each cell is represented by a <td>
.
So, in HTML, a simplified version of the above table would look like this:
<!-- <table> to surround everything -->
<table>
<!-- <tr> to represent a row -->
<tr>
<!-- <td> to represent a cell or column -->
<td>Stegosaurus</td>
<td>Late Jurassic</td>
<td>1877</td>
<td>9 m</td>
<td>4.5 t</td>
</tr>
<tr>
⋮
</tr>
⋮
</table>
<table>
— wraps around all the information in the chart.<tr>
— denotes a single row, no cell (<td>
) can be outside of a row.<td>
— represents the actual data, a cell, they must be inside a row.
Table headings
For better accessibility, we should label the headings of a table, both vertically and horizontally, as headings using the <th>
tag. The <th>
is just a specialized version of the <td>
with stronger semantics meaning “heading”.
In the sample table above that would be a cell like “Name” or “Stegosaurus”.
<th>Name</th>
or
<th>Stegosaurus</th>
But, for the most accessibility, we need to specify whether the heading labels a column, vertically, or labels a row, horizontally.
To label the direction, we use the scope
attribute:
scope="col"
— labels a heading for vertical columnsscope="row"
— labels a heading for horizontal rows
<th scope="col">Name</th>
<th scope="row">Stegosaurus</th>
Merging cells
We can merge cells in columns and rows and a combination of both. In the table above we can see that “Name”, “Period”, & “Discovery” are all merged vertically and “Sizes” is merged horizontally.
colspan
— allows us to merge cells together accross columns horizontallyrowspan
— allows us to merge cells together across rows vertically
The number of cells in each row and column should always add up to the same amount.
<th scope="col" rowspan="2">Name</th>
<th scope="col" colspan="2">Size</th>
Headers, body & footers
When we have more than one row representing the headers or rows that represent totals for the table it’s a good idea to group them together using <thead>
, <tbody>
and <tfoot>
.
<table>
<!-- <thead> to wrap around rows representing headers -->
<thead>
<tr>…</tr>
<tr>…</tr>
⋮
</thead>
<!-- <tbody> to wrap around the main content of the table -->
<tbody>
<tr>…</tr>
<tr>…</tr>
⋮
</tbody>
<!-- <tfoot> to wrap around rows representing footers, like totals, etc. -->
<tfoot>
<tr>…</tr>
<tr>…</tr>
⋮
</tfoot>
</table>
<thead>
— groups a bunch of rows together making them represent the “header” of the table.<tbody>
— groups a bunch of rows together making them represent the “main contenț” of the table.<tfoot>
— groups a bunch of rows together making them represent the “footer” or “totals” of the table.
All these elements must have <tr>
tags inside them—they are not a replacement for rows.
The <tfoot>
can actually be place above the <tbody>
in the HTML and it will still be rendered at the bottom of the table.
Styling tables
There are a few important CSS properties and ideas to know about when styling tables.
By default, the cells have spacing around them so borders are separated. Using CSS border-collapse
we can eliminate the extra space.
table {
border-collapse: collapse;
}
Aligning the text within cells can be done simply with vertical-align
.
th {
vertical-align: bottom; /* or middle, top */
}
Beyond those specialized CSS properties, tables can be treated just like any other element. Borders, fonts, classes, etc.
Cols & colgroups
You can style columns in the table without having to put a class on each cell.
At the top of the table, add a <col>
tag, with a class, and style that class. Styling the <col>
tag has some limitations: many CSS properties won’t work, it’s really intended for background-color
and a few others.
The <col>
tag is an empty tag—with absolutely no content. You can only add classes to it.
With this HTML:
<table>
<!-- You need a `<col>` for each cell -->
<col>
<col>
<col class="size">
<col>
<tr>
<td>…</td>
<td>…</td>
<td>…</td>
<td>…</td>
</tr>
</table>
You could do this in CSS:
.size {
background-color: limegreen;
}
If you want to group the column styles together, you can use <colgroup>
:
<table>
<colgroup>
<col>
<col>
</colgroup>
<col class="size">
<col>
<tr>
<td>…</td>
<td>…</td>
<td>…</td>
<td>…</td>
</tr>
</table>
Captioning a table
It’s usually a good idea to add a summary of the table’s contents—especially for accessibility reasons.
The summary is added using the <caption>
element:
<table>
<caption>Information on three awesome dinosaurs.</caption>
⋮
</table>
If people are using a screen reader they can listen to the caption and then choose whether they want to venture into the table for more information or just skip over it.
Elements inside cells
Inside the <td>
and <th>
elements you can put practically any HTML you want, including another table.
<table>
<tr>
<td>
<div>A div!</div>
<ul>
<li>A</li>
<li>Super</li>
<li>List</li>
</ul>
</td>
<td>
<p>A paragraph</p>
<time datetime="2233-03-22">Time element</time>
</td>
</tr>
</table>
Table in a table
Tables can be embedded inside of other tables, specifically inside another <td>
.
<table>
<tr>
<td>
<table>
<tr>
<td>Table-ception</td>
</tr>
</table>
</td>
</tr>
</table>
Video list
- Tables: Rows and columns
- Tables: Headings
- Tables: Merging cells with colspan and rowspan
- Tables: Headers, body, footers
- Tables: Styling
- Tables: Other elements in cells