Summary

Tables shouldn't be used for layout, as they can result in redundant information being announced as the user navigates through the document using a screen reader, such as "table with 3 columns and 4 rows column 1 row 2 ⋯". When nested tables are used, other redundant information is announced, such as "table nesting level 3 column 2 row 1 ⋯".

If tables are used for layout, then the WAI-ARIA presentation role can be used to remove the table semantics so the table information is no longer announced when the user navigates the page.

Supress table semantics with role="presentation"
<table role="presentation">
⋯
</table>

When controls are grouped in a fieldset element with a legend, the content of the legend element is announced by screen readers to provide context for the individual controls in the fieldset. This is particularly useful for controls like radio buttons where the group needs a label, as the individual labels for each radio button may not make sense on their own; for example, if the label is just "Yes" or "No". Unfortunately, if the form controls in the fieldset are in a layout table, then the legend is not announced as a label for the group in JAWS, even if the table has role="presentation".

Author: Gez Lemon

Layout tables

Although using role="presentation" stops redundant table information being announced to screen reader users, form controls within a fieldset are not reliably announced by JAWS with the context of the legend element to provide a label for the group. In the following example, when a JAWS user navigates through the radio buttons, they hear something like, "radio button not checked maybe 1 of 3", "radio button not checked yes 2 of 3", and "radio button not checked no 3 of 3" without the context of "Attending" (Firefox with JAWS provides the context when navigating forwards to a radio button in the group, but not when navigating backwards to a radio button in the group. IE with JAWS completely ignores the context provided by the legend element).

The legend element is ignored in JAWS
<fieldset>
<legend>Attending?</legend>
<table role="presentation">
<tr>
  <td>
    <input type="radio" 
           id="att1" 
           name="att" 
           value="maybe">
  </td>
  <td><label for="att1">Maybe</label></td>
</tr>
<tr>
  <td>
    <input type="radio" 
           id="att2" 
           name="att" 
           value="yes">
  </td>
  <td><label for="att2">Yes</label></td>
</tr>
<tr>
  <td>
    <input type="radio" 
           id="att3" 
           name="att" 
           value="no">
  </td>
  <td><label for="att3">No</label></td>
</tr>
</table>
</fieldset>

If the layout table isn't included, a JAWS user hears "Attending" as the context for the radio button in the group that receives focus. Continuing to navigate through the radio buttons in the group only announces the radio button information, as the context has already been provided.

Repair techniques

When form controls are in a layout table within fieldset/legend elements, the context information is lost in JAWS, even when the table semantics are removed with role="presentation". The obvious solution is to remove the layout table, but sometimes this option is too costly and developers just want a solution that works. One solution is to use WAI-ARIA's aria-describedby attribute. To do this, add an id attribute to the legend element, and use aria-describedby on the radio buttons that references the id attribute set on the legend element.

Using aria-describedby for a group label
<fieldset>
<legend id="attleg">Attending?</legend>
<table role="presentation">
<tr>
  <td>
    <input type="radio" 
           id="att1" 
           name="att" 
           value="maybe" 
           aria-describedby="attleg">
  </td>
  <td><label for="att1">Maybe</label></td>
</tr>
<tr>
  <td>
    <input type="radio" 
           id="att2" 
           name="att" value="yes" 
           aria-describedby="attleg">
  </td>
  <td><label for="att2">Yes</label></td>
</tr>
<tr>
  <td>
    <input type="radio"
           id="att3" 
           name="att" 
           value="no" 
           aria-describedby="attleg">
  </td>
  <td><label for="att3">No</label></td>
</tr>
</table>
</fieldset>

This technique works with JAWS, but requires using aria-describedby for each element in the fieldset. This approach also has the undesirable effect of announcing the context after the radio button information, rather than before the radio button, as the user would be used to with regular elements within a fieldset/legend section. It also means that screen readers that do announce the group label provided by the legend element also announce the description afterwards, which is overly verbose.

Results using aria-describedby as a repair technique

User agent Result
IE9 and IE10 with JAWS 13 "Attending?" Announced after the radio button information is announced.
IE9 and IE10 with NVDA "Attending?" announced as the label for the group, and announced again as the long description after the radio button information is announced.
Firefox 17 with JAWS 13 Announced after the radio button information is announced.
Firefox 17 with NVDA "Attending?" announced as the label for the group, and announced again as the long description after the radio button information is announced.

Ideally, we want a group label that is announced just once the first time the group is visited, so a more effective solution would be to use a role of group (or region if the section is significantly important) on the table element instead of the presentation role, and use aria-labelledby to provide the name for the group. Steve Faulkner pointed out that child element roles are unaffected by a parent role of group, so the table semantics are still present and need overriding with role="presentation". This should result in the same relationship as fieldset/legend without the layout table.

Using a role of group with a label for the group
<fieldset>
<legend id="attleg">Attending?</legend>
<table role="group" aria-labelledby="attleg">
<tbody role="presentation">
<tr role="presentation">
  <td role="presentation">
    <input type="radio" 
           id="att1"
           name="att"
           value="maybe">
  </td>
  <td role="presentation">
    <label for="att1">Maybe</label>
  </td>
</tr>
<tr role="presentation">
  <td role="presentation">
    <input type="radio" 
           id="att2" 
           name="att" 
           value="yes">
  </td>
  <td role="presentation">
    <label for="att2">Yes</label>
  </td>
</tr>
<tr role="presentation">
  <td role="presentation">
    <input type="radio" 
           id="att3" 
           name="att" 
           value="no">
  </td>
  <td role="presentation">
    <label for="att3">No</label>
  </td>
</tr>
</tbody>
</table>
</fieldset>

This arrangement works in Firefox, and ensures that the context is reliably provided before the radio button information just once when the group is visited forwards or backwards, in the same way as when the layout table isn't present with fieldset/legend. Unfortunately, it is completely ignored in IE with JAWS, and announced twice in browsers that do announce the legend content as a label for the group.

role="group" as a repair technique

User agent Result
IE9 and IE10 with JAWS 13 Group label not announced.
IE9 and IE10 with NVDA "Attending?" group label announced twice.
Firefox 17 with JAWS 13 Group label announced once.
Firefox 17 with NVDA "Attending?" group label announced twice.

As this attempt used lots of role="presentation" markup, I decided to wrap the table in a div element and moved the role="group" and aria-labelledby markup to the containing div element, and put the table role back to role="presentation". This arrangement didn't work either. After further discussion with Steve Faulkner, he pointed out that IE doesn't suppress the semantics of table cell elements when role="presentation" is used on the table element (it should do). I added role="presentation" to each of the table cells, and it finally worked as expected.

Nesting the table in a role of group
<fieldset>
<legend id="attleg">Attending?</legend>
<div role="group" aria-labelledby="attleg">
  <table role="presentation">
  <tr>
    <td role="presentation">
      <input type="radio" 
             id="att1"
             name="att"
             value="maybe">
    </td>
    <td role="presentation">
      <label for="att1">Maybe</label>
    </td>
  </tr>
  <tr>
    <td role="presentation">
      <input type="radio" 
             id="att2" 
             name="att" 
             value="yes">
    </td>
    <td role="presentation">
      <label for="att2">Yes</label>
    </td>
  </tr>
  <tr>
    <td role="presentation">
      <input type="radio" 
             id="att3" 
             name="att" 
             value="no">
    </td>
    <td role="presentation">
      <label for="att3">No</label>
    </td>
  </tr>
  </table>
</div>
</fieldset>

Using role="presentation" on table cells in a table wrapped in a group element

User agent Result
IE9 and IE10 with JAWS 13 "Attending?" announced as the group label.
IE9 and IE10 with NVDA "Attending?" group label announced twice.
Firefox 17 with JAWS 13 "Attending?" announced as group label twice when navigating forwards, and one when navigating backwards in to the group.
Firefox 17 with NVDA "Attending?" group label announced twice.

Conclusion

The table element should only be used for describing data tables, rather than using tables for layout purposes. If layout tables absolutely cannot be avoided, then using role="presentation" is effective at ensuring that redundant table information isn't announced to screen reader users. However, this can't be relied on alone when form controls within fieldset/legend elements are in layout tables for JAWS users.

If it's impossible to avoid layout tables for the whole website, at least avoid using tables for layout within fieldset/legend elements. If this isn't possible, then the repair technique is to wrap the table in an element with role="group" and an aria-labelledby attribute that identifies the group label (the legend), and the table element and each table cell must be overridden with role="presentation".

The repair technique for ensuring that form controls in a layout table within fieldset/legend elements is probably more complicated than not using layout tables in the first place, and duplicates the group label for assistive technologies that correctly ignore layout tables with role="presentation". Sometimes, it's difficult to find a repair technique that works in all situations, but using the most semantically appropriate elements will always result in a more accessible experience.

Category: Accessibility.

Comments

  1. [layout_tables_repair_techniques.php#comment1]

    > [...] to remove the layout table, but sometimes this option is too costly [...]

    Not only costy. Also sometimes it's impossible to achieve certain layout (fluid ones with a footer) without tables.

    Posted by D on

  2. [layout_tables_repair_techniques.php#comment3]

    Hi D,

    For at least 8 years, probably more, there have been numerous articles on how to achieve multi-column layouts with footers using CSS without using layout tables.

    Hi Leif,

    The role of the input element is not an issue. The role is relayed correctly. The problem is with the group label when the elements in the group are contained in a layout table. The roles of the elements in the table are correct, including the accessible name, but the group label required for context for each element in the group (usually from the legend element) is lost.

    Posted by Gez on

  3. [layout_tables_repair_techniques.php#comment4]

    Hi Gez,

    Right. But it is actually a bit difficult to understand the problem. Is it so that Jaws, without the repair, simply jumps over the legend?

    In the summary you seem to hint that the legend sounds *different*, without the repair.(So, it is not simply jumped over.) Quote: "not announced as a label for the group". (Thus it could be announced, but "not as a label".)

    However, I have tested your code example in Windows 7 with IE9 and Jaws 13, and I am not able to hear any difference whether I use a table with role=presentation or whether I remove the table completely. Should I try another OS/UA/Jaws combination to experience the error?

    Btw, part of my confusion is probably that in VoiceOver, then the legend is announced once for each radio button plus once at the start of the form field. So it is read 4 times. This is not too far from what you achieve with aria-describedby in the first repair technique — but which you considered too much/too wordy.

    Posted by Leif Halvard Silli on

  4. [layout_tables_repair_techniques.php#comment5]

    @Gez

    Yes but I have yet to find one tableless layout that is 100% fluid.
    Point me one please.

    (And besides, frankly, using floats for columns and semantically useless <div> wrappers is a more terrible hack than <table role=presentation> )

    Posted by D on

  5. [layout_tables_repair_techniques.php#comment6]

    Thank you for explaining this in such detail. Timely for me - I just accepted a freelance job to completely revamp a website designed in 1999 using tables for their catalog.
    Wendy

    Posted by Wendy on

Comments are closed for this entry.