See plans and pricing

Table headers must have associated data cells

Estimated effort: about 10 minutes.

A <th> element is meant to be a header for one or more actual data cells in a table. If a table's headers are marked up incorrectly — for example a <th> that uses a headers attribute pointing nowhere, or a table where header relationships aren't defined through scope at all — screen readers can't announce which header belongs to which row of data, which matters most on an order-history table with many rows of dates, statuses, and totals.

How to fix it

  1. Find data tables where header cells (<th>) don't clearly connect to the data cells (<td>) below or beside them, commonly an order-history table listing past orders with columns like Order, Date, Status, and Total.
  2. For simple tables with one row of column headers and no merged cells, add scope="col" to each <th> in the header row, and scope="row" to any <th> used as a row header (for example an order number acting as the row label).
  3. Only use <th> elements for actual headers — do not use a <th> for a cell that is just visually bold or styled to look like a header, and do not leave a <th> present with no data cells for it to describe.
  4. If a <th> uses the headers attribute rather than scope, make sure it is not required to have the attribute at all — per this rule, <th> elements should use scope, not headers, and should only appear when there is a single row and column of headers.
  5. Test the corrected table with a screen reader's table navigation mode to confirm each data cell is announced with its correct column/row header.

WooCommerce note

WooCommerce's default cart table template (templates/cart/cart.php) gives every column header cell in the table's thead row a scope="col" attribute (Remove item, Thumbnail image, Product, Price, Quantity, Subtotal), which satisfies this rule out of the box. If this issue appears on a cart, checkout, or custom order-history table, check for a theme override of that template, or a custom order-history/reports table, that dropped the scope attributes.

Before and after

Before

<table>
  <tr>
    <th>Order</th>
    <th>Date</th>
    <th>Status</th>
    <th>Total</th>
  </tr>
  <tr>
    <td>#1042</td>
    <td>Aug 12, 2026</td>
    <td>Shipped</td>
    <td>$48.00</td>
  </tr>
</table>

After

<table>
  <tr>
    <th scope="col">Order</th>
    <th scope="col">Date</th>
    <th scope="col">Status</th>
    <th scope="col">Total</th>
  </tr>
  <tr>
    <td>#1042</td>
    <td>Aug 12, 2026</td>
    <td>Shipped</td>
    <td>$48.00</td>
  </tr>
</table>

View the full rule reference from Deque University

See plans and pricing