To associate HTML values with a header covering several rows, define a row group and use scope="rowgroup". For more complex relationships, give headers unique IDs and list them in each data cell’s headers attribute. This guide builds a local example in TextEdit on Mac.

The task is to create one HTML table whose values retain their context when a label spans multiple rows. W3C documents grouped headers in its irregular tables tutorial and explicit associations in its multi-level headers tutorial.

Which association does your table need?

Start by writing down the labels needed to understand one value. In the synthetic example below, the value 40 means the width, in millimetres, of the large variant in set Alpha. Its complete context is therefore Alpha, Large, and Width. All dimensions are illustrative sample data.

Use this decision aid before editing:

Table structure Association to use Structural requirement
A header labels one ordinary row scope="row" Put the header in a th cell in that row
A header labels one ordinary column scope="col" Put the header in a th cell in that column
A header labels an entire group of body rows scope="rowgroup" Enclose those rows in their own tbody
A value needs several headers, or relationships change within the table Unique header IDs and a headers list Identify every applicable header explicitly

The scope values describe the relationship; rowspan describes how many rows the cell occupies. A tall header still needs the appropriate association. MDN documents both attributes separately in its table header element reference.

For a regular group, W3C’s pattern combines a spanning header with a separate tbody and scope="rowgroup". A table can contain several body groups. Keep header rows in thead; a header spanning rows in the footer belongs in tfoot. W3C: Tables with Irregular Headers.

For complicated tables, explicit associations let you state the whole meaning of a value directly. W3C recommends this approach for patterns including changing column headers and cells associated with three or more headers. The complete example uses explicit lists alongside structural scope attributes so that each numeric value names its three headers. W3C: Tables with Multi-Level Headers.

Create the HTML document in TextEdit

Use a new local file for the exercise. If you are adapting an existing document, first keep a separate copy so that you can compare its original values and ordering with your edits.

  1. Open TextEdit and choose File > New.
  2. Choose Format > Make Plain Text.
  3. Paste the complete document below, excluding the Markdown fence markers.
  4. Choose File > Save and name it table-headers.html.
  5. If prompted about the extension, choose Use .html.

These are Apple’s documented steps for creating an HTML file in TextEdit. The example needs no packages, scripts, or external assets.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Sample tile dimensions</title>
  <style>
    table { border-collapse: collapse; }
    th, td { border: 1px solid #777; padding: 0.5rem; }
    caption { text-align: left; font-weight: bold; }
  </style>
</head>
<body>
  <h1>Sample tile dimensions</h1>
  <table>
    <caption>Illustrative tile dimensions in millimetres</caption>
    <thead>
      <tr>
        <th scope="col">Set</th>
        <th scope="col">Variant</th>
        <th id="tiles-width" scope="col">Width</th>
        <th id="tiles-height" scope="col">Height</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th id="tiles-alpha" rowspan="2" scope="rowgroup">Alpha</th>
        <th id="tiles-alpha-small" scope="row">Small</th>
        <td headers="tiles-alpha tiles-alpha-small tiles-width">20</td>
        <td headers="tiles-alpha tiles-alpha-small tiles-height">30</td>
      </tr>
      <tr>
        <th id="tiles-alpha-large" scope="row">Large</th>
        <td headers="tiles-alpha tiles-alpha-large tiles-width">40</td>
        <td headers="tiles-alpha tiles-alpha-large tiles-height">60</td>
      </tr>
    </tbody>
    <tbody>
      <tr>
        <th id="tiles-beta" rowspan="2" scope="rowgroup">Beta</th>
        <th id="tiles-beta-small" scope="row">Small</th>
        <td headers="tiles-beta tiles-beta-small tiles-width">25</td>
        <td headers="tiles-beta tiles-beta-small tiles-height">35</td>
      </tr>
      <tr>
        <th id="tiles-beta-large" scope="row">Large</th>
        <td headers="tiles-beta tiles-beta-large tiles-width">45</td>
        <td headers="tiles-beta tiles-beta-large tiles-height">65</td>
      </tr>
    </tbody>
  </table>
</body>
</html>

To preview the result, open the saved file in Safari. You should see four columns, with Alpha and Beta each occupying two body rows. Keep the file available in TextEdit for the source checks below.

When reopening the document in TextEdit, use File > Open, select the file, click Show Options, and select Ignore rich text commands. Alternatively, enable Display HTML files as HTML code instead of formatted text under TextEdit > Settings, then Open and Save. Apple documents these options in its HTML editing instructions.

How does the example connect each value to its headers?

Read the source for 40 as a small association map. Its headers list contains tiles-alpha, tiles-alpha-large, and tiles-width. Those IDs identify Alpha, Large, and Width respectively. The corresponding height value, 60, keeps the first two IDs and substitutes tiles-height for the third.

The IDs are author-chosen identifiers, not special HTML keywords. Each listed token must identify a th element in the same table. Separate tokens with spaces, and do not repeat tokens within one list. These requirements come from the HTML Standard’s common table-cell attributes.

The list spells out the complete context rather than expecting the reader to reconstruct it through another header. W3C’s H43 technique describes putting the applicable header IDs in each data cell’s headers attribute.

Notice that the second Alpha row begins with Large. It contains no extra cell for Alpha: the first row’s rowspan="2" already occupies that position. Adding an empty cell there would alter the grid. MDN explains that cells fit into the available positions left by spanning cells in its header-cell usage notes.

Use the same reasoning to distinguish two superficially similar layouts. A left-side label spanning several body rows can be a row-group header. A column label spanning two rows at the top can still be a column header with scope="col"; its physical height does not turn its relationship into a row-group relationship. MDN: Header attributes.

Check associations before changing the table

Use a three-label audit for this example. Choose a value, identify its intended set, variant, and dimension, then compare those labels with the referenced IDs.

Value Intended context Expected headers value
20 Alpha, Small, Width tiles-alpha tiles-alpha-small tiles-width
60 Alpha, Large, Height tiles-alpha tiles-alpha-large tiles-height
25 Beta, Small, Width tiles-beta tiles-beta-small tiles-width
65 Beta, Large, Height tiles-beta tiles-beta-large tiles-height

Repeat that check for every numeric cell. Confirm that every referenced ID exists, every reference points to the intended header, and no ID is duplicated elsewhere in the document. These checks follow the procedure in W3C’s H43 technique. A visually tidy table is only one part of the check; inspect the source associations too.

When adding a third Alpha variant, insert its row inside Alpha’s existing tbody, change Alpha’s span to rowspan="3", give the new variant a unique ID, and add the appropriate three-header list to each new numeric cell. Preserve the original row order and values while making that change.

When removing a variant, reduce the span accordingly. If you remove the first row of a group, move its group header into the first remaining row. Keep the spanning cell inside one row group: the HTML table model does not allow a cell to extend across multiple row groups. WHATWG: Table model.

If rows are later generated or reordered by an application, treat these as update requirements: keep each group together, place its spanning header in its first row, maintain the span count, and update any changed references. For this example, moving Large before Small within Alpha would also require moving the Alpha header to that new first row.

What if the editor cannot preserve these attributes?

An editor’s ability to merge cells does not establish that it preserves the associations used here. Before relying on an edited or exported document, inspect its HTML for the intended group boundaries, scopes, IDs, and header lists. This tutorial’s supported Mac workflow is direct source editing in TextEdit.

If your authoring tool cannot retain the required structure, consider a separate, simpler table for each set, with Variant, Width, and Height columns. W3C presents splitting complex tables into smaller tables as an alternative that can make both reading and authoring easier. Use that option only when changing the original structure is acceptable. W3C: Split up multi-level tables.

For the completed example, pick any number and verify that its three referenced labels describe exactly what it measures. Then keep that same check alongside every row insertion, deletion, or reorder. It gives you a concrete way to preserve the table’s meaning as well as its appearance.

Sources