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 10 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.