Skip to content
easeweb
Search topics

Layout tables read as data tables, or out of order

A newsletter lays its products out with tables, so a screen reader reads each product as a column header, or reads every name before any price.

WCAG 2.2
SC 1.3.1ASC 1.3.2A
Required by
Section 508 · EN 301 549 (EU)
Broken <tr><td>$18</td><td>$32</td>…</tr>
The accessibility problem illustrated: No vision:Which price is the tote's?
01

The problem

Screen reader users hear a newsletter's products announced as a data table, or hear every name before any price, and can't tell which price is whose.

02

Try it

Find the linen tote’s price from what a screen reader reads, not from the email.

  • Broken 1: the panel lists what a screen reader reads from the top of the products. What does it say they are?

  • Broken 2: the email looks the same. Read the panel: which price is the tote’s?

  • Apply a fix. Read the panel again: each product is read whole, before the next.

Broken Can you fix this?

Reading from the top, a screen reader says:

  1. Table, 3 columns, 1 row
  2. Column header, Enamel mug, $18
  3. Column header, Linen tote, $32
  4. Column header, Speckled mug, $24
  5. Out of table

A recreated demo. The broken version is intentionally inaccessible.

03

The fix

Read each product whole, with no table around it.

Before

Each column a header cell

<table class="row">  <tr>    <th class="columns">      <table>        <tr>          <th>            <img src="enamel-mug.jpg" alt="">            <p>Enamel mug</p>            <p>$18</p>          </th>        </tr>      </table>    </th>    <th class="columns">      <table>        <tr>          <th>            <img src="linen-tote.jpg" alt="">            <p>Linen tote</p>            <p>$32</p>          </th>        </tr>      </table>    </th>    …  </tr></table>
What is wrong3 points

The order is right: each product is read whole, in one cell. The cells are where it fails.

  • Each column is a th, so the browser treats the outer table as a data table, and a screen reader announces a table with its rows and columns before the products.
  • Each product is read as a column header, which heads nothing.
  • No table has role="presentation" to say it only places content.
After

One product to a cell, in tables marked as presentation

<table class="row" role="presentation">  <tr>    <td class="columns">      <table role="presentation">        <tr>          <td>            <img src="enamel-mug.jpg" alt="">            <p>Enamel mug</p>            <p>$18</p>          </td>        </tr>      </table>    </td>    <td class="columns">      <table role="presentation">        <tr>          <td>            <img src="linen-tote.jpg" alt="">            <p>Linen tote</p>            <p>$32</p>          </td>        </tr>      </table>    </td>    …  </tr></table>
How it is fixed3 steps
  1. Make every cell a td. Nothing in the grid heads anything, so nothing is a th.
  2. Put each product's photo, name and price in one cell, so it is read whole, and the next product starts only after it.
  3. Give every table role="presentation", the nested ones too. The role reaches the table's own rows and cells but not a table inside them. A screen reader now reads the three products as plain content, with no table announced.

This fixes both Befores and passes 1.3.1 and 1.3.2, as long as every table the template writes for layout has the role, including the wrappers, spacers and buttons that emails draw with tables.

Easily confused
04

Verify the fix

5 checks, no mouse.

Test with: Screen reader, Styles off, Zoom

0 of 5 checked

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the template changes.

Limits

This is a starting pattern, not a guarantee for every email. Mail clients differ, and a webmail service shows the email inside its own page, so test in the clients your customers use. Whether a table without a role is announced depends on the guesses of each browser and screen reader. Reading order on a page laid out with CSS, where the order on screen is changed by the layout rather than by a table, is covered in reading order that doesn't match the layout.

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

EaseWeb service

Fixed it? Keep it from coming back.

I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.

See the service