Skip to content
AgileLogics
5 min read

Complex Tables

Prerequisites: Tables

Simple tables need only th with scope. Tables with grouped or multi-level headers need more.

Column groups and header groups

<table>
  <caption>Quarterly revenue (thousands of euros)</caption>
  <colgroup>
    <col>
    <col span="2" class="first-half">
    <col span="2" class="second-half">
  </colgroup>
  <thead>
    <tr>
      <td></td>
      <th scope="colgroup" colspan="2">First half</th>
      <th scope="colgroup" colspan="2">Second half</th>
    </tr>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Q1</th>
      <th scope="col">Q2</th>
      <th scope="col">Q3</th>
      <th scope="col">Q4</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">North</th>
      <td>120</td><td>135</td><td>128</td><td>150</td>
    </tr>
    <tr>
      <th scope="row">South</th>
      <td>98</td><td>101</td><td>115</td><td>122</td>
    </tr>
  </tbody>
</table>
  • scope="colgroup" says the header labels a group of columns.
  • colgroup / col describe columns so CSS can style whole columns (only a few properties apply — background, border, width, visibility).
  • The empty top-left cell is a td, not an empty th.

Row groups

Several tbody elements split rows into groups; a th with scope="rowgroup" labels one:

<table>
  <caption>Staff directory</caption>
  <thead>
    <tr><th scope="col">Name</th><th scope="col">Extension</th></tr>
  </thead>
  <tbody>
    <tr><th scope="rowgroup" colspan="2">Sales</th></tr>
    <tr><td>Ana</td><td>201</td></tr>
    <tr><td>Ben</td><td>202</td></tr>
  </tbody>
  <tbody>
    <tr><th scope="rowgroup" colspan="2">Support</th></tr>
    <tr><td>Chen</td><td>301</td></tr>
  </tbody>
</table>

headers and id

When scope can't describe the relationships (irregular tables), each data cell can list its header cells explicitly:

<table>
  <caption>Exam results</caption>
  <tr>
    <td></td>
    <th id="math">Math</th>
    <th id="art">Art</th>
  </tr>
  <tr>
    <th id="ana">Ana</th>
    <td headers="ana math">A</td>
    <td headers="ana art">B</td>
  </tr>
</table>

This works but is laborious. If a table needs it, consider splitting it into several simpler tables.

Wide tables on small screens

Wrap a wide table in a scrollable, focusable region so it doesn't break the page layout:

<div role="region" aria-labelledby="t-cap" tabindex="0" class="table-scroll">
  <table>
    <caption id="t-cap">Train timetable</caption>
    <tr><th scope="col">Train</th><th scope="col">Departs</th></tr>
    <tr><td>IC 101</td><td>08:15</td></tr>
  </table>
</div>

With CSS overflow-x: auto, keyboard users can focus the region and scroll it with the arrow keys.

Related developer tools