TechLead

Tables and Structured Data

Proper table markup for data, not layout

HTML tables present two-dimensional data with rows and columns. They are the correct tool for tabular data — schedules, financial figures, comparison grids — but should never be used for page layout (that is what CSS Grid and Flexbox are for).

Basic Table Structure

A well-structured table separates its head, body, and foot using semantic section elements.

<table>
  <caption>Q1 Sales by Region</caption>

  <thead>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Units Sold</th>
      <th scope="col">Revenue</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>North</td>
      <td>1,240</td>
      <td>$62,000</td>
    </tr>
    <tr>
      <td>South</td>
      <td>980</td>
      <td>$49,000</td>
    </tr>
  </tbody>

  <tfoot>
    <tr>
      <td>Total</td>
      <td>2,220</td>
      <td>$111,000</td>
    </tr>
  </tfoot>
</table>

Spanning Cells

colspan and rowspan let a single cell occupy multiple columns or rows.

<table>
  <thead>
    <tr>
      <th rowspan="2">Name</th>
      <th colspan="2">Scores</th>
    </tr>
    <tr>
      <th>Math</th>
      <th>Science</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>95</td>
      <td>88</td>
    </tr>
  </tbody>
</table>

Accessibility

Screen readers navigate tables by headers. Use scope on every <th> and <caption> or aria-label on the table. For complex tables with multi-level headers, use id and headers attributes to explicitly associate cells.

<!-- Simple table: use scope -->
<th scope="col">Product</th>
<th scope="row">January</th>

<!-- Complex table: use id + headers -->
<th id="h-q1">Q1</th>
<th id="h-north">North</th>
<td headers="h-q1 h-north">$42,000</td>

Styling Tables with CSS

table {
  border-collapse: collapse;  /* removes double borders */
  width: 100%;
}

th, td {
  border: 1px solid #ddd;
  padding: 0.75rem 1rem;
  text-align: left;
}

thead th { background: #f5f5f5; font-weight: 600; }

/* Zebra striping */
tbody tr:nth-child(even) { background: #fafafa; }

/* Sticky header on scrollable table */
thead th { position: sticky; top: 0; z-index: 1; }

Responsive Tables

Wide tables overflow on small screens. Wrap the table in a horizontally scrollable container rather than reflowing the table structure.

<div style="overflow-x: auto; -webkit-overflow-scrolling: touch;">
  <table>
    <!-- wide table content -->
  </table>
</div>

colgroup for Column Styling

<table>
  <colgroup>
    <col style="width: 40%">
    <col style="width: 30%; background: #fffbeb">
    <col style="width: 30%">
  </colgroup>
  <!-- ... -->
</table>

Best Practices

  • Always use <caption> — it is the table's accessible name
  • Use <th> for headers, never <td> styled to look like a header
  • Keep tables simple — if a cell needs a nested table, reconsider the data model
  • Sort and filter large tables with JavaScript, not by re-rendering HTML

Continue Learning