6 minutes reading time (1103 words)

Accessible Tables

Accessible Tables

What does a value in a table cell mean? Simply look at the header at the top of the column and/or somewhere at the beginning of the row. But what if you are blind?

When I talk about HTML tables, I don't mean how we used them for layout-purposes in the beginning of the internet. I only mean tabular data, where the table headers indicate what the meaning is of the data in the cell. 

We have for instance this table from the Canine Research Center with some data about their dogs:

🐕 Canine Research Center: Physical & Behavioral Data 2025
Dog Name Height (cm) Length (cm) Tail Length (cm) Daily Food (kg) Favorite Food Bark Volume (dB)
Biscuit 45 68 22 1.2 Chicken & Rice 85
Max 62 92 35 2.1 Beef Stew 95
Jumbo 320 600 150 150 Hay & Vegetables 117
Mr. Whiskers 25 45 28 0.3 Tuna & Mice 45
Daisy 35 52 19 0.8 Lamb & Sweet Potato 76
Rocky 58 85 32 1.8 Raw Human Legs 92
Bella 42 61 24 1.1 Duck & Blueberry 81

If you can see the screen, you see that Daisy’s tail length is 19 cm. An other row is an other dog. An other column is other data about that dog. If you don't know to what row or column a cell belongs, you only see data, but don't know what dog it is about and what it says about that dog. There has to be a connection between the cell and the headers.

scope attribute

Hundreds of millions cannot see a screen. Many visually impaired people have to read a webpage with a screen reader. A screen reader is software that reads the page aloud. If you do not relate the table cells to the headers, the screen reader would just read the values, which makes their meaning hard to understand.

So you have to associate the cells with the appropriate headers: the above value 19 is then read as: Daisy, tail length centimeter, 19.

To connect the headers with the columns and rows, you have to use the scope attribute in the table header (th) elements. On the top row in the above table the column headers look like this:

  <thead>
      <tr>
        <th scope="col">Dog Name</th>
        <th scope="col">Height (cm)</th>
        <th scope="col">Length (cm)</th>
        <th scope="col">Tail Length (cm)</th>
        <th scope="col">Daily Food (kg)</th>
        <th scope="col">Favorite Food</th>
        <th scope="col">Bark Volume (dB)</th>
      </tr>
    </thead>

row headers

The names of the dogs in the above table are the row headers. To indicate that they are headers, you have to put them in a table header (th) element too, not a table data (td) element. With the scope attribute you can indicate that it is a header for a row. So Biscuit’s row in the above table is:

     <tr>
        <th scope="row">Biscuit</th>
        <td>45</td>
        <td>68</td>
        <td>22</td>
        <td>1.2</td>
        <td>Chicken & Rice</td>
        <td>85</td>
      </tr>

In Joomla’s backend such a row header often is not in the first column. The list views in the Administrator side have a row header in the fifth or sixth column. Most of the time it is the column that refers to a form to edit the item. Also such a row header that is not in the first column should be a table header (th) element, with scope=”row”.

Only real headers should be a table header (th) element.  For instance in Joomla’s backend list views the first cell is to select all checkboxes in the column beneath it. That is not a table header (th) element:

   <td class="w-1 text-center">
       <?php echo HTMLHelper::_('grid.checkall'); ?>
   </td>

Cells that are not table header (th) elements should be table data (td) elements. For good accessibility a header (th) element should always have a scope attribute attached, to indicate if it is a header for the column or the row. 

Joomla core is accessible

The above rules have been applied in Joomla CMS. The list views in the Administrator for instance all have the correct td and th elements, and all th elements have a scope attribute. Joohoo!


This article is mainly written to stimulate extension developers to also apply those rules. To make their extensions accessible. I didn’t find it in the documentation or tools for Joomla extension development (yet).

More complex tables

The scope attribute has two more possible values: ”colgroup” and “rowgroup”. With that you can assign headers to multiple columns or rows. Currently, that is not used anywhere in Joomla CMS. And that is good: the general advice is to avoid complex tables. If possible try to split them into multiple tables that are more simple.

With the scope attribute you connect the table headers with the columns and rows. The other way around is also possible: by adding a headers attribute to a table data (td) element. This is less used than the scope attribute in the table header (th) element, and should only be used in rare occasions when it is hardly possible to connect the cell and the header via the scope attribute. In Joomla CMS it is currently only used in /layouts/joomla/form/field/rules.php, although I suspect that we can easily apply scope=”col” there too, like in the rest of the CMS.

Sections

You should divide tables into three sections: thead, tbody, and tfooter. This structural division gives a better overview, styling possibilities, and is useful if you want to repeat headers and footers, for instance when printing multiple pages. But this division, although good practice, has no influence on how it is interpreted by screen readers.

Caption

A caption element is important for accessibility. It must be placed directly after the table tag. You can put relevant information there, what the table is about. And you can add information about filtering and ordering. In Joomla core that caption has a class="visually-hidden”.  In Joomla core the (multilingual) caption for a table of articles looks like:

 <caption class="visually-hidden">
   <?php echo Text::_('COM_CONTENT_ARTICLES_TABLE_CAPTION'); ?>,
   <span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
   <span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>

Summary

In a HTML table:

  1. Check that all header cells are th elements. Non header cells (that can also be in the thead section!) should be td elements. Don’t forget the row headers.
  2. Check that all th elements have a scope attribute.
  3. Check that all scope attributes have the value row, col, rowgroup, or colgroup. Preferably only row or col.
  4. Put a caption element just under the table element.

Also see this WCAG-page about the scope attribute

 

Some articles published on the Joomla Community Magazine represent the personal opinion or experience of the Author on the specific topic and might not be aligned to the official position of the Joomla Project

3
The November Issue
JoomlaDay France 2025 – Metz, an edition focused o...
 

Comments

Already Registered? Login Here
No comments made yet. Be the first to submit a comment

By accepting you will be accessing a service provided by a third-party external to https://magazine.joomla.org/