Skip to main content
← πŸ“„ HTML & the platformΒ·Module A4 Β· Lesson 2
TaskBuild a 2-year quarterly comparison. Top header row: empty cell, then <th colspan='2' scope='colgroup'>2025</th>, <th colspan='2' scope='colgroup'>2026</th>. Second header row: Region, Q1, Q2, Q1, Q2 β€” all scope='col'. One body row: <th scope='row'>EU</th> + four data cells.

Complex tables: colspan, rowspan, multi-level headers

100 XP9 min
Theory

When one cell spans multiple columns or rows

<table>
  <thead>
    <tr>
      <th></th>
      <th colspan="2" scope="colgroup">2025</th>
      <th colspan="2" scope="colgroup">2026</th>
    </tr>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Q1</th>
      <th scope="col">Q2</th>
      <th scope="col">Q1</th>
      <th scope="col">Q2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">EU</th>
      <td>120</td><td>150</td><td>200</td><td>240</td>
    </tr>
  </tbody>
</table>

scope=colgroup / scope=rowgroup

When a header spans multiple columns (year heading over four quarter columns), use scope="colgroup" instead of scope="col". Same for rowgroup when a row header spans multiple body rows.

colspan and rowspan

<td colspan="3">Spans 3 columns</td>
<td rowspan="2">Spans 2 rows</td>

Used in headers AND data cells. Spanning data: think "merged cells" in Excel.

Empty top-left corner

When the very top-left cell of a table-with-row-headers is intentionally empty, just <th></th> is fine. Don't put a description there β€” screen readers will read it. Empty cell signals "no header for this column."

πŸ”’

Sign up to start coding

Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β€” card required, cancel anytime.

Sign up β€” free trial β†’

First 15 lessons in each track are free. No card needed for those.

← PreviousNext lesson β†’

Get one Python or web tip a day β€” by email

Short, hand-written, no spam. Unsubscribe in one click.