Complex Tables Done Right

Merge cells with colspan and rowspan, and make headers meaningful with scope.

Data tables get hard when a single value applies to several columns or rows. Two attributes cover almost every real-world grid.

Spanning cells

  • colspan="n" — the cell stretches across n columns.
  • rowspan="n" — the cell stretches down n rows.

When a cell spans, delete the cells it now covers — a classic bug is leaving them in and pushing the grid out of alignment.

The attribute nobody sets: scope

On a header cell, scope="col" or scope="row" tells assistive tech which cells that header describes. In a plain grid the browser can guess, but the moment you have both row and column headers, scope is what keeps a screen reader from reading nonsense.

<th scope="col">Q1</th>
<th scope="row">Revenue</th>

Add a <caption> too — it's the table's accessible name and costs one line.

Example

Try it yourself
Loading editor…
Press Run to see the result.

When to use it

  • A complex schedule grid uses scope="col" and scope="row" on <th> elements so screen readers announce the correct header for each cell.
  • A data table uses headers and id attributes to explicitly link cells to their column and row headers in complex merged layouts.
  • An accessibility audit flags tables missing scope attributes as failures; a developer adds them to achieve WCAG 1.3.1 compliance.

More examples

Table headers with scope attributes

scope="col" on thead headers and scope="row" on row headers explicitly map each data cell to both its headers.

Example · html
<table>
  <caption>Course Enrollment by Term</caption>
  <thead>
    <tr>
      <th scope="col">Course</th>
      <th scope="col">Spring</th>
      <th scope="col">Fall</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">HTML</th>
      <td>142</td>
      <td>208</td>
    </tr>
    <tr>
      <th scope="row">CSS</th>
      <td>110</td>
      <td>189</td>
    </tr>
  </tbody>
</table>

Table with id and headers for complex layout

The headers attribute references multiple th ids, allowing screen readers to read all applicable headers for a cell.

Example · html
<table>
  <tr>
    <th id="loc">Location</th>
    <th id="q1">Q1</th>
    <th id="q2">Q2</th>
  </tr>
  <tr>
    <th id="north">North</th>
    <td headers="north q1">$12k</td>
    <td headers="north q2">$15k</td>
  </tr>
</table>

Summary table with colgroup styling

<colgroup> and <col> set column widths; scope attributes on every header ensure screen reader accessibility.

Example · html
<table>
  <colgroup>
    <col style="width:40%">
    <col style="width:30%">
    <col style="width:30%; font-weight:bold;">
  </colgroup>
  <thead>
    <tr>
      <th scope="col">Item</th>
      <th scope="col">Unit Price</th>
      <th scope="col">Total</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Pro Plan</th>
      <td>$19/mo</td>
      <td>$228/yr</td>
    </tr>
  </tbody>
</table>

Discussion

  • Be the first to comment on this lesson.
Complex Tables Done Right — HTML | SoundsCode