Matheion

MA-WEB1 · Module 6

Tables

Topics: table, tr, td, th · colspan and rowspan · thead/tbody/tfoot · Data grids

Overview

Session 6 — Tables

You will present tabular data with the table element: rows (tr), data cells (td), header cells (th with scope), spanning with colspan/rowspan, and the thead/tbody/tfoot wrappers that name a table's parts. The discipline: tables are for data grids, not page layout, and every header should declare whether it labels its column or its row.

Plan: ideas → worked tables → bridge → short drills → reveal.

Time: about 55–65 minutes if you draw every grid cell by cell before reading the reveal.

Concepts

Session 6 — Tables

The one-paragraph story

Some content is naturally a grid — prices by drink and size, opening hours by day, scores by team. The table element lays out exactly that: a <table> containing <tr> rows, each row holding <td> cells, with <th> for header cells that describe a row or column. The optional thead/tbody/tfoot wrappers label the table's parts, and colspan/rowspan let one cell cover several columns or rows. The discipline: use tables for data, not for page layout (layout is CSS's job in later sessions), and always label your headers so the grid can be read by everyone. This session builds the Harbor Café's price board as a proper table.


The basic anatomy

<table>
  <tr>
    <th>Drink</th>
    <th>Small</th>
    <th>Large</th>
  </tr>
  <tr>
    <td>Espresso</td>
    <td>2.50</td>
    <td>3.20</td>
  </tr>
  <tr>
    <td>Latte</td>
    <td>3.40</td>
    <td>4.10</td>
  </tr>
</table>

Three nesting levels, read inside out:

  • tr — a table row.
  • td — a table data cell inside a row.
  • th — a header cell (bold, centred by default). Headers may sit in the first row (column labels) or the first column (row labels).

The grid's shape is defined purely by these cells: three rows of three cells make a 3×3 grid, with the first row's cells being headers. There are no "column" elements — a column is whatever the cells at the same position across rows happen to be.


Spanning: one cell, several positions

A cell can cover more than one grid position:

<tr>
  <th>Seasonal special</th>
  <td colspan="2">Pumpkin spice latte</td>
</tr>

colspan="2" makes the "Pumpkin spice latte" cell stretch across two columns — so this row contributes one header cell plus one wide data cell. rowspan="2" does the same vertically:

<tr>
  <th rowspan="2">House drinks</th>
  <td>Espresso</td>
</tr>
<tr>
  <td>Americano</td>
</tr>

The "House drinks" header covers both rows. When you span, count carefully: the total grid columns must stay consistent, or the table becomes lopsided — a cell that started in column 2 may silently shift.


thead, tbody, tfoot: naming the parts

A long table benefits from grouping its rows:

<table>
  <thead>
    <tr>
      <th>Drink</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Espresso</td>
      <td>2.50</td>
    </tr>
    <tr>
      <td>Latte</td>
      <td>3.40</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td>Drip coffee</td>
      <td>2.00</td>
    </tr>
  </tfoot>
</table>
  • thead — the header rows (column labels).
  • tbody — the data rows.
  • tfoot — summary/footer rows.

The wrappers label the parts of the grid. Styling later targets them as groups — a different background for the header, a border above the footer — and screen readers can announce them. Note the order: thead, then tbody, then tfoot (even though visually the footer is at the bottom).


What a header cell should say

A th with a scope attribute declares what it heads:

<th scope="col">Small</th>
<th scope="row">Espresso</th>

scope="col" says "this header describes its column"; scope="row" says "this one describes its row". Screen readers use the scope to connect each cell with its labels when reading a grid aloud — the difference between hearing "3.40" and hearing "Latte, large: 3.40".


Tables for data, not for layout

An older practice used tables to arrange the whole page — a left cell for the nav, a middle cell for content. That is what the layout sessions of this course (13–16) exist to replace with CSS. The rule: tables are for tabular data. If the content is a grid of facts (prices by drink, hours by day), a table is right. If you are arranging blocks on a page, that is layout, and CSS does it better and more accessibly.


Pitfalls to watch for

  • A row's cells must line up. Forget a td or misapply a colspan, and the grid shifts.
  • th labels, td holds data. Using td for headers loses the accessible meaning.
  • thead/tbody/tfoot are optional but meaningful — use them once a table grows beyond a few rows.
  • Do not use tables to lay out the page — that is CSS's job.

Examples

Session 6 — Worked examples

Example A — The café price board

<table>
  <thead>
    <tr>
      <th scope="col">Drink</th>
      <th scope="col">Small</th>
      <th scope="col">Large</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Espresso</th>
      <td>2.50</td>
      <td>3.20</td>
    </tr>
    <tr>
      <th scope="row">Latte</th>
      <td>3.40</td>
      <td>4.10</td>
    </tr>
    <tr>
      <th scope="row">Flat white</th>
      <td>3.40</td>
      <td>4.10</td>
    </tr>
  </tbody>
</table>

Read the grid:

  • thead holds the column labels (Drink / Small / Large), each th scope="col".
  • Each tbody row starts with a row label (th scope="row": Espresso, Latte, Flat white) and then two data cells (td).
  • The structure is 3 columns × 4 rows. Every row has exactly three cells, so the grid stays square.

The scope attributes let a screen reader announce "Latte, small: 3.40" instead of a bare number.


Example B — colspan: a special spanning one row

<table>
  <tr>
    <th scope="col">Offer</th>
    <th scope="col">Price</th>
  </tr>
  <tr>
    <td>Morning bun + drip coffee</td>
    <td>4.50</td>
  </tr>
  <tr>
    <td colspan="2">Seasonal special: pumpkin spice latte — ask at the counter</td>
  </tr>
</table>

The last row has one cell spanning both columns, so the special's note runs across the whole table width. Count the columns: the other rows each have two cells; the spanning row has one cell that counts as two — the grid stays 2 columns wide.


Example C — rowspan: a label across rows

<table>
  <tr>
    <th scope="col">Category</th>
    <th scope="col">Item</th>
    <th scope="col">Price</th>
  </tr>
  <tr>
    <th scope="row" rowspan="2">Coffee</th>
    <td>Espresso</td>
    <td>2.50</td>
  </tr>
  <tr>
    <td>Americano</td>
    <td>3.00</td>
  </tr>
</table>

The "Coffee" label covers both rows; each row still has its own item and price cells. Check the column count: the header row has 3 cells; the first data row has 1 (spanning 2 rows) + 2 = 3 columns; the second row has 2 cells which fill the columns left by the span. Consistent.


Example D — Hours table with thead/tbody/tfoot

<table>
  <thead>
    <tr>
      <th scope="col">Day</th>
      <th scope="col">Kitchen</th>
      <th scope="col">Bar</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Mon–Thu</th>
      <td>8:00–14:00</td>
      <td>7:00–15:00</td>
    </tr>
    <tr>
      <th scope="row">Fri</th>
      <td>8:00–20:00</td>
      <td>7:00–22:00</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Sat–Sun</th>
      <td>9:00–15:00</td>
      <td>9:00–16:00</td>
    </tr>
  </tfoot>
</table>

The weekend row sits in tfoot — semantically a footer, visually the last row. The tbody rows are the "normal" hours; the tfoot is the exceptional case. Reading order in the source is thead, tbody, tfoot; the browser draws tfoot at the bottom.


Example E — Why not use tables for layout

<!-- Anti-pattern: page layout via table -->
<table>
  <tr>
    <td>Navigation links</td>
    <td>Main content here</td>
  </tr>
</table>

This works visually, but it abuses the grid — the "table" is not tabular data, the columns cannot adapt to small screens, and screen readers announce a meaningless grid. The CSS layout sessions (13–16) replace this with flexible columns that reflow. The rule: tabular data → table; page structure → CSS.

Practice check — tables

Multiple-choice check from the Matheion app lesson. Pick an answer for each question, then submit.

  1. 1.In a table, a header cell that labels a column should be written:

  2. 2.`colspan="2"` on a cell means:

  3. 3.The correct source order of a table's part-wrappers is:

  4. 4.A `rowspan="2"` header is appropriate when:

  5. 5.Using a `table` to arrange a page's navigation and content columns is: