The theme_table() function doesn't include semantic table headings in Core. For WCAG validation it is important to add semantic information to data tables.

This has been added in the Views module #864006: Improve table semantics by adding scope or headers/id attributes which is great, but it should also be built into theme_table() so that programmatically.

Previous patches here have used scope="col" since it was easier to implement, but the Views solution for table data already in Core uses the header/id approach so it would be good to be consistent.

There are advantages to both, but for this application it would probably be best to be consistent.

Taking from the WebAim description (see the link below):

"The purpose of data tables is to present information in a grid, or matrix, and to have column or rows that show the meaning of the information in the grid. When screen readers read straight through data tables—especially large ones—it's easy for users to get lost.

In order for a data table to be accessible, it must have the proper markup in the HTML. When the proper HTML markup is in place, users of screen readers can navigate through data tables one cell at a time, and they will hear the column and row headers spoken to them."

---

From the A11ySprint discussion we postponed this issue #1096124: Accessibility: Compose tips tables need better use of id's & summary elements because theme table needs to be able to support the scope and header relationship attributes. Also discussed the need for a scope attribute in #1413670: Document 'show row weights' mode
http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_table/7

More information about this is described here:
http://tink.co.uk/2010/08/screen-reader-support-for-html-tables/
http://webaim.org/techniques/tables/data

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mgifford’s picture

Status: Active » Needs review
FileSize
686 bytes

Adding scope="col" has got to be redundant when looking at a TD that's nicely wrapped in a THEAD.

Can we reliably add scope="row" to the first column of all of the TBODY rows? Not sure that is adding much either. Although it is used in the example - http://www.access-board.gov/sec508/guide/1194.22.htm

ARIA added as per:
http://www.w3.org/WAI/PF/aria/roles#columnheader

Not sure that the complex tables described in the Webaim article can be done without a customized theme_table().

I thought we'd discussed tying the TBODY cell to a header id but can't see a requirement for that now.

Status: Needs review » Needs work

The last submitted patch, table-semantics-1645308-1.patch, failed testing.

BarisW’s picture

Status: Needs work » Needs review
FileSize
2.47 KB

Would be nice if can tell theme_table which column is the one to be used for the row attribute (which is the key column).
See this example: http://www.w3.org/TR/WCAG20-TECHS/H63#H63-examples

So I took the following approach. When setting the $header for the table, you can also define which header is the primary identifier. I'm not sure if this is the best way to it, or if we want to introduce a new attribute which holds the primary column name.

If you define the table like this:

<?php
  $header = array(
    t('User ID'),
    array(
      'data' => t('Name'),
      'primary' => TRUE,
    ),
    t('Age'),
    t('Sex'),
  );
  $rows = array(
    array(1, 'user1', 18, t('Male')),
    array(2, 'user2', 17, t('Female')),
    array(3, 'user3', 20, t('Male')),
  );
  $table = theme('table', array('rows' => $rows, 'header' => $header));
?>

This will result in the following output:

<table class="sticky-enabled tableheader-processed sticky-table">
 <thead><tr><th>User ID</th><th>Name</th><th>Age</th><th>Sex</th> </tr></thead>
<tbody>
 <tr class="odd">
   <td>1</td>
   <td scope="row">user1</td>
   <td>18</td>
   <td>Male</td>
 </tr>
 <tr class="even">
   <td>2</td>
   <td scope="row">user2</td>
   <td>17</td>
   <td>Female</td>
 </tr>
 <tr class="odd">
   <td>3</td>
   <td scope="row">user3</td>
   <td>20</td>
   <td>Male</td>
 </tr>
</tbody>
</table>

If you don't define a primary column, the first column will get the scope attribute.

The last submitted patch, semantic-tables-1645308-3.patch, failed testing.

BarisW’s picture

What's wrong?

BarisW’s picture

Issue tags: -Accessibility, -data table

#3: semantic-tables-1645308-3.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Accessibility, +data table

The last submitted patch, semantic-tables-1645308-3.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
FileSize
2.07 KB

I noticed the patch in #1 wasn't included.

Like the changes you made for rows, tried to apply it to my local git repo and it failed. So I re-rolled it (with #1 included).

To add the following to the output:

<tr class="odd">
   <td  scope="col" role="columnheader">1</td>
   <td scope="row" role="rowheader" scope="col" role="columnheader">user1</td>
   <td  scope="col" role="columnheader">18</td>
   <td  scope="col" role="columnheader">Male</td>
</tr>

I also added the ARIA role "rowheader" - https://wiki.mozilla.org/Accessibility/TableHeaders & http://www.w3.org/TR/wai-aria/roles

Status: Needs review » Needs work

The last submitted patch, semantic-tables-1645308-8a.patch, failed testing.

BarisW’s picture

As far as I understand the ARIA roles, they only make sense when you don't use the semantic elements themselves. The examples they give apply roles to div's, not to td's. So I'm not sure if need all the extra HTML?

mgifford’s picture

Good question.. Less is almost always better, but I'll try to get some feedback on this.

mgifford’s picture

I asked Paul Jackson and got the following:

"Are you looking for feedback on the use of WAI-ARIA on tables? If so, I agree with BarisW that we should avoid using ARIA to enforce existing semantics as it doesn't add anything and would make file sizes bigger (to the detriment of mobile). I only use WAI-ARIA to override the defaults for a semantic element or to add semantics where it doesn't already exist. In the case of a regulat table, I wouldn't use table WAI-ARIA but would if I'm trying to emulate a table using divs and CSS."

This approach makes sense to me. I'll re-roll the patch.

mgifford’s picture

Status: Needs work » Needs review
FileSize
2.03 KB

Status: Needs review » Needs work
Issue tags: -Accessibility, -data table

The last submitted patch, semantic-tables-1645308-13.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review

#13: semantic-tables-1645308-13.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Accessibility, +data table

The last submitted patch, semantic-tables-1645308-13.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review

There's been a lot of discussion in Views about table attributes #864006: Improve table semantics by adding scope or headers/id attributes.

In the end though I don't see how we can do much more than add the scope="col" attribute to the table element.

For validation for accessibility guidelines this is very useful.

The scope attribute is only valid for the TH attribute, so I've adjusted the HTML accordingly. I don't see any way to add <th scope="row"> in D7 or D8. You'd need to have some way of specifying which column includes header information as far as I see it.

Feedback on this simpler patch would be appreciated (and the Views table issue).

mgifford’s picture

woops. forgot to attach the patch.

Status: Needs review » Needs work

The last submitted patch, semantic-tables-1645308-17.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
FileSize
2.61 KB

Hopefully this patch does it.

There's still a discussions about how to address the rows to add the scope. Paul Jackson suggested:
"Maybe a reasonable solution would be to have an option to make the first cell in each secondary row into a TH. It would be set at the table level to save the user time (so don't have to manually add for each row)."

However, I think that might be best to take on as a separate issue.

Status: Needs review » Needs work

The last submitted patch, semantic-tables-1645308-20.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
FileSize
211.75 KB
3.51 KB

Ok, I got Paul's suggestion working, but it does look kinda silly. The TH is formatted like a header when most of the time we wouldn't want it to be.

I guess there could be some work arounds for this via css... Also digging into the table code I can see that you can really make more complicated tables here. However, not sure how many are used in core. Most in core are pretty simple, with the most complicated thing being some colspans here/there.

Status: Needs review » Needs work

The last submitted patch, semantic-tables-1645308-22.patch, failed testing.

mgifford’s picture

Title: Scope & Semantic Header Relationships Needed in theme_table() » Add Scope or Headers/id to associate the data cells with the appropriate headers in theme_table()
Issue tags: +semantic markup

This will help meet Section 508 guidelines as well as WCAG 2.0AA.

Also, wanted to change the name to indicate that there are two recommended ways to associate the data cells with the appropriate headers as per http://webaim.org/techniques/tables/data/#id

BarisW’s picture

Re #17:

"The scope attribute is only valid for the TH attribute, so I've adjusted the HTML accordingly."

See http://www.w3.org/TR/html4/struct/tables.html#course-ex

It's perfectly valid to add a scope attribute to a <td>, and it can be used to define which cell in that row defines the scope. My patch from #3 combined with your patch from #22 would make a table like showed in the URL given above.

mgifford’s picture

It got depreciated in HTML5. The thread in Views got interesting about here:
https://drupal.org/node/864006#comment-6457058

With D7 one could still add a TD based scope attribute, but that's not an option for D8. At least not at this point.

mgifford’s picture

Status: Needs work » Needs review
FileSize
3.74 KB

Re-rolling the patch to keep up with Core.

Status: Needs review » Needs work

The last submitted patch, semantic-tables-1645308-27.patch, failed testing.

mgifford’s picture

I'm coming to conclude that something in simpletest doesn't like scope="col" being added to a header:

I went here:

    // Test the table header.
    $elements = $this->xpath('//div[@id="content"]//table/thead/tr/th');
    $this->assertEqual(count($elements), 4, 'Correct number of table header cells found.');

Looked at the results locally. Adjusted the number of columns based on the test from 3 to 4. This is still giving me grief.

What's the best way to count the headers if xpath is failing me?

mgifford’s picture

Status: Needs work » Closed (duplicate)
mgifford’s picture

Issue summary: View changes

adding relationship to another issue.

mgifford’s picture

Issue summary: View changes
Status: Closed (duplicate) » Active

That issue ultimately was tied to just Views tables, so... Reopening this one looking at theme_table().

mgifford’s picture

Title: Add Scope or Headers/id to associate the data cells with the appropriate headers in theme_table() » Add Headers/id to associate the data cells with the appropriate headers in theme_table()

What happened to the function theme_table()? There are certainly still references in the comments.

Closest I could find was function template_preprocess_table().

This should follow the same approach as what we implemented in Views, so using Headers & ID's.

mgifford’s picture

Issue tags: +TwinCities

Another good one to sprint on over the weekend.

mgifford’s picture

Issue tags: -TwinCities +TCDrupal 2014

Wrong name...

mgifford’s picture

Issue tags: +dcamsa11y
BarisW’s picture

Can someone update the issue description? What exactly is the requested output?

mgifford’s picture

mgifford’s picture

Issue summary: View changes
mgifford’s picture

Title: Add Headers/id to associate the data cells with the appropriate headers in theme_table() » Improve table semantics by adding scope or headers/id attributes to theme_table()
Issue summary: View changes
mgifford’s picture

Status: Active » Closed (works as designed)

theme_table() no longer exists in D8 so I am going to close this issue.