Mark up tables semantically to define the relationship between header cells and data cells.
For simple tables with one row of column headers and/or one column of row headers,
use the <table>, <tr>, <th>, and <td> elements for mark up.
use the scope attribute to associate headers with their data cells and any parent headers
For complex tables, use id and headers attributes for explicit associations.
Canada.ca Content Style Guide
Use tables to organize and present data
Value of each cell relates to the column and row headers
Entries in a column don't contain information that could be considered a subhead
Value of each cell aligns with the column header that appears directly above it
Give your table a clear title (using <caption>) that describes the information in it
Convert complex tables into one or more simple tables
Convert a table to a list if the data is simple
Irregular tables
In irregular tables, represent the relationship between row- and column-spanning headers and their data cells with the scope attribute:
When a header spans multiple columns, add the scope="colgroup" attribute to the <th> element.
When a header spans multiple rows, add the scope="rowgroup" attribute to the <th> element.
Define groups in the table with <colgroup> and <tbody> elements:
Define column groups with the <colgroup> element, solo columns with <col>, as first children of the table element.
Define row groups with the <tbody> element. The solo <thead> and <tfoot> elements define their own groups.
Tables with multi-level headers
To associate multi-level headers with data cells:
Give each header a unique id attribute value.
Reference those id attribute values in the headers attribute of data cells defined by the header.
The id/headers technique is not currently (2021) well-supported by screen readers. Simplify the data presentation by breaking a complex table into two or more simpler tables.
Captions and summary
Use the <caption> element to provide a name or title for the table
If the caption duplicates the preceding heading, visually-hide the <caption> with the WET CSS class .wb-inv.
Place the <caption> element as a direct child of the <table> element
Provide a summary for complex tables that includes purpose, composition, trends and/or usage
Wherever possible, convert complex tables into simple tables or lists, so that less or no explanation is required.
Do no duplicate caption information in the summary.
Pick a method for presenting table summaries:
Nest the summary inside the <caption> element
Set the summary in a paragraph preceding or following the table. Reference the summary from the table with the aria-describedby attribute.
Use the <figure> element to mark up a table summary. Use aria-labelledby and aria-describedby attributes to associate the caption and summary, respectively, with the table.
Additional tips and tricks
Break up a complex table into simpler smaller tables, each containing data for one sub-topic of the original table.
Start a new <table> when the topic changes.
Separate each piece of data into its own cell.
Don’t use line breaks (<br> elements) to create table rows.
Align text to the left and numeric data to the right.
Differentiate header <th> and data <td> cells visually.
Style even and odd rows in a different way to improve readability.
Ensure table isn’t cut off (e.g., do not use overflow: hidden in CSS).
Avoid using tables for layout. Use CSS for layout instead.