Multi-level headers

Designing with multi-level headers

A table with multi-level headers has headers stacked three deep, or column headers that repeat or change partway down a table.

To associate multi-level headers with data cells,

  • Give each header a unique id attribute value.
  • Reference those id attribute values in the headers attribute of data cells.

Using the id/headers attributes technique is a last resort, as it's not currently (2021) well-supported by assistive technologies. While the “good” example table below is technically compliant, it performs poorly in screen readers.

Important: Whenever possible, simplify the data presentation by breaking a complex table into two or more simpler tables.

A table summary may be necessary. Column headers that repeat or change partway down a table can easily surprise screen readers users as they descend rows. The same is true of column-spanning row headers. In the table summary, mention their presence.

  Good example: Table with headers three deep

In this example, the row headers are three deep, requiring the use of id/headers markup to associate the data cells with their headers. The WET Table Validator provides the id/headers markup. A visually-hidden table description is nested in the <caption> element.

Vehicles leased by the Government Column 1 lists executive and non-executive vehicles. Column 2 lists the country (Canada or the USA). Column 3 lists the term of lease (long-term or short term). Column 4 lists the policy requirement.
Type of Vehicle Country Term of Lease Policy Requirement
executive CANADA long-term

Comprehensive commercial insurance, including collision and third party liability;

- self-underwrite the deductible -

short-term

Comprehensive commercial insurance, including collision and third party liability;

- self-underwrite the deductible -

U.S. long-term

Comprehensive commercial insurance, including collision and third party liability;

- self-underwrite the deductible -

short-term

Purchase additional commercial insurance to cover third party liability and collision for the U.S. risks;

- self-underwrite the deductibles -

non-executive CANADA long-term

Self-underwrite except if provincial legislation applies

short-term

Comprehensive commercial insurance, including collision and third party liability;

- self-underwrite the deductible -

U.S. long-term

Purchase additional commercial insurance to cover third party liability and collision for the U.S. risks;

- self-underwrite any damage to government vehicle -

short-term

Utilize commercial insurance coverage (third party liability and collision for the U.S. risks) administered by Services and Specialized Acquisitions Management Sector, PWGSC;

- self-underwrite the deductible -

View HTML snippet - Assigning id and headers attributes
<tr>
   <th id="tbl6">Type of Vehicle</th>
   <th id="tbl7">Country</th>
   <th id="tbl8">Term of Lease</th>
   <th id="tbl9">Policy Requirement</th>
</tr>
<tr>
   <th rowspan="4" id="tbl12" headers="tbl6">executive</th>
   <th rowspan="2" id="tbl13" headers="tbl7 tbl12">CANADA</th>
   <th id="tbl14" headers="tbl8 tbl12 tbl13">long-term</th>
   <td headers="tbl9 tbl12 tbl13 tbl14">
      <p>Comprehensive commercial insurance, including collision and third party liability;</p> 
      <p>- self-underwrite the deductible -</p>
   </td>
</tr>
View complete HTML
<table>
   <caption>
      Vehicles leased by the Government
      <span class="wb-inv">Column 1 lists executive and non-executive vehicles. Column 2 lists the country (Canada or the USA). Column 3 lists the term of lease (long-term or short term). Column 4 lists the policy requirement.</span>
   </caption>
   <tbody>
      <tr>
       <th id="tbl6">Type of Vehicle</th>
       <th id="tbl7">Country</th>
       <th id="tbl8">Term of Lease</th>
       <th id="tbl9">Policy Requirement</th>
      </tr>
      <tr>
       <th rowspan="4" id="tbl12" headers="tbl6">executive</th>
       <th rowspan="2" id="tbl13" headers="tbl7 tbl12">CANADA</th>
       <th id="tbl14" headers="tbl8 tbl12 tbl13">long-term</th>
       <td headers="tbl9 tbl12 tbl13 tbl14">
          <p>Comprehensive commercial insurance, including collision and third party liability;</p> 
          <p>- self-underwrite the deductible -</p>
       </td>
      </tr>
      <tr>
       <th id="tbl25" headers="tbl8">short-term</th>
       <td headers="tbl9 tbl12 tbl13 tbl25">
          <p>Comprehensive commercial insurance, including collision and third party liability;</p> 
          <p>- self-underwrite the deductible -</p>
       </td>
      </tr>
      <tr>
       <th rowspan="2" id="tbl30" headers="tbl7">U.S.</th>
       <th id="tbl31" headers="tbl8 tbl30">long-term</th>
       <td headers="tbl9 tbl12 tbl30 tbl31">
          <p>Comprehensive commercial insurance, including collision and third party liability;</p> 
          <p>- self-underwrite the deductible -</p>
       </td>
      </tr>
      <tr>
       <th id="tbl36" headers="tbl8">short-term</th>
       <td headers="tbl9 tbl12 tbl30 tbl36">
          <p>Purchase additional commercial insurance to cover third&nbsp;party liability and collision for the U.S. risks;</p> 
          <p>- self-underwrite the deductibles -</p>
       </td>
      </tr>
      <tr>
       <th rowspan="4" id="tbl41" headers="tbl6">non-executive</th>
       <th rowspan="2" id="tbl42" headers="tbl7 tbl41">CANADA
       </th><th id="tbl43" headers="tbl8 tbl41 tbl42">long-term</th>
       <td headers="tbl9 tbl41 tbl42 tbl43">
          <p>Self-underwrite except if provincial legislation applies</p>
       </td>
      </tr>
      <tr>
       <th id="tbl48" headers="tbl8">short-term</th>
       <td headers="tbl9 tbl41 tbl42 tbl48">
          <p>Comprehensive commercial insurance, including collision and third party liability;</p> 
          <p>- self-underwrite the deductible -</p>
       </td>
      </tr>
      <tr>
       <th rowspan="2" id="tbl53" headers="tbl7">U.S.</th>
       <th id="tbl54" headers="tbl8 tbl53">long-term</th>
       <td headers="tbl9 tbl41 tbl53 tbl54">
          <p>Purchase additional commercial insurance to cover third party liability and collision for the U.S. risks;</p> 
          <p>- self-underwrite any damage to government vehicle -</p>
       </td>
      </tr>
      <tr>
       <th id="tbl59" headers="tbl8">short-term</th>
       <td headers="tbl9 tbl41 tbl53 tbl59">
          <p>Utilize commercial insurance coverage (third party liability and collision for the U.S. risks) administered by Services and Specialized Acquisitions Management Sector, <abbr title="Public Works and Government Services Canada">PWGSC</abbr>;</p>
          <p>- self-underwrite the deductible -</p>
       </td>
      </tr>
   </tbody>
</table>

  Good example: Multi-level table simplified

The table in the previous example is overly complex, requiring the poorly-supported id/headers technique. A better approach is to split a complex table into two or more smaller tables. In this example, the “Type of Vehicle" (executive or non-executive) is moved from column 1 to the table <caption>, resulting in one table about executive vehicles and a second table about non-executive vehicles. The simpler tables consist of two levels of headers, so can be rendered using the scope attribute rather than with id/headers attributes. The scope method is easier to author and maintain, and is better supported by user agents than the id/headers method.

Non-executive Vehicles leased by the Government
Country Term of Lease Policy Requirement
CANADA long-term

Self-underwrite except if provincial legislation applies

short-term

Comprehensive commercial insurance, including collision and third party liability;

- self-underwrite the deductible -

U.S. long-term

Purchase additional commercial insurance to cover third party liability and collision for the U.S. risks;

- self-underwrite any damage to government vehicle -

short-term

Utilize commercial insurance coverage (third party liability and collision for the U.S. risks) administered by Services and Specialized Acquisitions Management Sector, PWGSC;

- self-underwrite the deductible -

View HTML
<table>
   <caption>Non-executive Vehicles leased by the Government</caption>
   <thead>
       <tr>
            <th scope="col">Country</th>
            <th scope="col">Term of Lease</th>
            <th scope="col">Policy Requirement</th>
       </tr>
   </thead>
   <tbody>
       <tr>
            <th rowspan="2" scope="rowgroup">CANADA</th>
            <th scope="row">long-term</th>
            <td>
                <p>Self-underwrite except if provincial legislation applies</p>
            </td>
       </tr>
       <tr>
            <th scope="row">short-term</th>
            <td>
                <p>Comprehensive commercial insurance, including collision and third party liability;</p>
                <p>- self-underwrite the deductible -</p>
            </td>
       </tr>
   </tbody>
   <tbody>
       <tr>
            <th rowspan="2" scope="rowgroup">U.S.</th>
            <th scope="row">long-term</th>
            <td>
                <p>Purchase additional commercial insurance to cover third party liability and collision for the U.S. risks;</p>
                <p>- self-underwrite any damage to government vehicle -</p>
            </td>
       </tr>
       <tr>
            <th scope="row">short-term</th>
            <td>
                <p>Utilize commercial insurance coverage (third party liability and collision for the U.S. risks) administered by Services and Specialized Acquisitions Management Sector, <abbr title="Public Works and Government Services Canada">PWGSC</abbr>;</p>
                <p>- self-underwrite the deductible -</p>
            </td>
       </tr>
   </tbody>
</table>

In this example, the table headers “Organic (1kg)" and “Non-organic (1kg)" serve as subheadings to describe the next section of the table. Using the headers attribute, all three headers are properly associated with the data cell.

This example sets the table in a <figure> element, as described in the section Caption & summary. Rather than use a <caption> element, the <figcaption> holds the table title, in a <strong> element, as well as a table summary to aid comprehension. The aria-labelledby and aria-describedby attributes on the <table> element point to the title and summary, respectively, which makes the associations stronger and more reliable in assistive technologies.

Note that the empty header cell is given an id attribute value and a non-breaking space as content. The cell is referenced by top-level headers in their headers attribute. This prevents some assistive technologies from declaring the cell.

Cost of organic vs non-organic produce in Canada, the USA and the UK

Column one lists organic produce followed by non-organic, other columns show the cost by country.

  Canada USA UK
Organic (1kg)
Apples $3.62 $4.87 $2.69
Bananas $1.47 $1.68 $1.60
Onions $2.28 $2.81 $1.44
Non-organic (1kg)
Apples $3.37 $4.53 $2.50
Bananas $1.37 $1.56 $1.49
Onions $2.12 $2.61 $1.34
View HTML snippet - Assigning id attributes to <th> cells
<thead>
   <tr>
      <td id="blank">&nbsp;</td>
      <th id="can" scope="col" headers="blank">Canada</th>
      <th id="usa" scope="col" headers="blank">USA</th>
      <th id="uk" scope="col" headers="blank">UK</th>
   </tr>
<thead>
<tbody>
   <tr>
      <th id="org" class="span" colspan="4" scope="colgroup" headers="blank">
         Organic (1kg)
      </th>
   </tr>
   <tr>
      <th id="o-apples" headers="org">
         Apples
      </th>
      […]
   </tr>
   […]
</tbody>
View HTML snippet - Assigning headers attributes to <td> cells
[…]
<td headers="org o-apples can">$3.62</td>
<td headers="org o-apples usa">$4.87</td>
[…]
View complete HTML
<figure> 
   <figcaption>  
      <strong id="caption">Cost of organic vs non-organic produce in Canada, the USA and the UK</strong>  
      <p id="summary">Column one lists organic produce followed by non-organic, other columns show the cost by country.</p>  
   </figcaption>  
   <table aria-labelledby="caption" aria-describedby="summary">   
      <thead>  
        <tr> 
          <td id="blank">&nbsp;</td>  
          <th headers="blank" id="can">Canada</th> 
          <th headers="blank" id="usa">USA</th> 
          <th headers="blank" id="uk">UK</th> 
        </tr> 
      </thead> 
      <tbody> 
         <tr>  
            <th headers="blank" id="org" class="span" colspan="4">Organic</th> 
         </tr> 
         <tr> 
            <th headers="org" id="o-apples">Apples</th> 
            <td headers="org o-apples can">$3.62</td> 
            <td headers="org o-apples usa">$4.87</td> 
            <td headers="org o-apples uk">$2.69</td> 
         </tr> 
         <tr> 
            <th headers="org" id="o-bananas">Bananas</th>  
            <td headers="org o-bananas can"> $1.47</td> 
            <td headers="org o-bananas usa">$1.68</td> 
            <td headers="org o-bananas uk">$1.60</td> 
         </tr> 
         <tr> 
            <th headers="org" id="o-onions">Onions</th> 
            <td headers="org o-onions can">$2.28</td> 
            <td headers="org o-onions usa">$2.81</td> 
            <td headers="org o-onions uk">$1.44</td>  
         </tr> 
         <tr> 
            <th headers="blank" id="non-org" class="span" colspan="4">Non-organic</th> 
         </tr>  
         <tr>  
            <th id="n-apples" headers="non-org">Apples</th> 
            <td headers="non-org n-apples can">$3.37</td> 
            <td headers="non-org n-apples usa">$4.53</td> 
            <td headers="non-org n-apples uk">$2.50</td> 
         </tr>  
         <tr> 
            <th id="n-bananas" headers="non-org">Bananas</th> 
            <td headers="non-org n-bananas can">$1.37</td> 
            <td headers="non-org n-bananas usa">$1.56</td>  
            <td headers="non-org n-bananas uk">$1.49</td>  
         </tr>  
         <tr>  
            <th id="n-onions" headers="non-org">Onions</th>  
            <td headers="non-org n-onions can">$2.12</td> 
            <td headers="non-org n-onions usa">$2.61</td>  
            <td headers="non-org n-onions uk"> $1.34</td>  
         </tr>  
      </tbody>  
   </table>  
</figure>

Related WCAG resources

Related WCAG resources

Success criteria

Techniques

Back to top