vaadin / web-components

A set of high-quality standards based web components for enterprise web applications. Part of Vaadin 20+
https://vaadin.com/docs/latest/components
446 stars 83 forks source link

grid: Incorrect header structure #93

Open TetraLogicalHelpdesk opened 3 years ago

TetraLogicalHelpdesk commented 3 years ago

WCAG Level

Level A

Priority

High

Pages/screens/components affected

Description

In the Column Grouping example for the grid, there are two separate rows for headers. The top row defines overarching column group headers. Despite the use of aria-colspan, it appears that the structure is not correctly reflected in the final table output. As a result, when navigating through data cells in the 4th column (which structurally comes under "Name > Last"), the cell is announced as belonging to "Location > Last" by assistive technologies.

The column grouping example, with a cell in the 4th column highlighted

Current output of NVDA when focusing the cell:

Arnaud  not selected  row 3  Location
Last  column 4

User impact

Assistive technology users will have difficulty navigating through the grid and understanding the data if the header relationship is incorrectly conveyed.

Required solution

Ensure that the relationship between the group headers, the sub-headers, and the data cells is correctly conveyed.

This solution must be applied to all instances of the issue identified within the test sample, then applied to all other instances of the same issue identified throughout the rest of the components, their variants, and the documentation website.

Implementation guidance

It was not possible to determine exactly what is causing this mismatch. Looking through the actual <table> structure that the component generates in the shadow DOM, colspan and aria-colspan appear to be correctly placed to convey the intended grouping. Nonetheless, the structure is consistently announced incorrect in various browser/assistive technology combinations (e.g. Chrome/NVDA, Firefox/JAWS). We recommend reviewing how the structure of this component is set up in the shadow DOM - in case a detail escaped us during testing. This may be an issue/bug in current browsers and their interpretation of the header relationships, or in current assistive technologies and their interpretation of the aria-colspan attribute.

While not ideal, as a workaround, it may be necessary to explicitly add a headers attribute to all individual data cells, referencing the group header and the column-specific header, to ensure that the hierarchy/relationship is correctly conveyed.

<td id="vaadin-grid-cell-39" ... headers="vaadin-grid-cell-2 vaadin-grid-cell-7">...</td>

Test procedure(s)

Use these steps to confirm that the solution has been correctly applied to issues identified within the test sample, and to test the rest of the components, their variants, and the documentation website for instances of the same issue:

  1. Using a screen reader, navigate through a grid with column groups.
  2. Verify that the group header and column header for each data cell are announced correctly.

Definition of done

Complete all of these tasks before closing this issue or indicating it is ready for retest:

Related standards

More information

Test data

Test date: March 2021 Website: vaadin.com/components, vaadin.com/docs-beta

tomivirkki commented 3 years ago

This has something to do with the header rows having display: flex applied. The issue can be reproduced with a regular <table> element.

The following example works as expected on NVDA (the focused cell announces as: "VoiceOver IOS Safari"), but as soon as I change the first header row to use display: flex, it announces as "TalkBack IOS Safari":

<table>
  <thead>
    <!-- <tr style="display: flex"> -->
    <tr>
      <th scope="row">Screen Reader</th>
      <th colspan="2" scope="colgroup" id="group">VoiceOver</th>
      <th colspan="2" scope="colgroup">TalkBack</th>
    </tr>
    <tr>
      <th scope="row">Browser</th>
      <th scope="col">macOS Safari</th>
      <th scope="col" id="column">iOS Safari</th>
      <th scope="col">Chrome</th>
      <th scope="col">Firefox</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">aria-expanded</th>
      <td>Yes</td>
      <td tabindex="0" headers="group column">Yes</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
  </tbody>
</table>

Tried applying the header attribute on the body cell element, referencing the correct header cells ("group" and "column"), but this doesn't seem to change the behaviour.

patrickhlauke commented 3 years ago

display: flex can often lead to problems with intrinsic/native roles of elements not being recognised anymore. It may require explicitly re-adding the role="..." to elements - see https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html

tomivirkki commented 3 years ago

Tried applying the proper roles to the table structure elements, but still the wrong header group title is announced: https://jsbin.com/zaronobeme/2/edit?html,output

Unfortunately, not using display: flex isn't an option for us.

tomivirkki commented 3 years ago

One workaround would be to generate hidden (yet aria-hidden="false") cells, with aria-labelledby, next to the header/footer cells that have colspan assigned. This will fix the issue with the screen readers but as a drawback the <table>'s semantic coherence suffers (the additional elements shouldn't be there).

https://jsbin.com/cometaheme/1/edit?html,output

patrickhlauke commented 3 years ago

reposting here from slack:

just tried https://jsbin.com/cometaheme/1/edit?html,output in chrome/nvda and firefox/nvda, and was getting odd readings. in chrome/nvda, the headers attribute seemed to be ignored. navigating through the table with table-reading keys, the "VoiceOver" and "TalkBack" first-level headers were only announced on the cell that was directly below that header, i.e. VoiceOver macOS Safari / TalkBack Chrome. not for the other ones. in Firefox/nvda, the headers attribute was recognised and used, but only that was announced ... so the yellow highlighted cell is just announced as "VoiceOver - Yes"

Admittedly this is where you're likely bumping into not only issues of screen readers, but also issues of browsers and how they interpret/misinterpret which header cells apply where.

As a last resort, I'd try relying completely on headers and being over-explicit for each cell about "this is the set of headers that applies here". i.e. giving each <th> its own unique id, and for each cell (including second-level header cells) specifying a (space-separated) list of headers that apply explicitly to that cell

tomivirkki commented 3 years ago

The headers approach didn't work as already mentioned here https://github.com/vaadin/web-components/issues/93#issuecomment-817683599

I tried some more combinations and the best one I found was with having the <thead> role cleared with role="presentation" (to mitigate the issues screen readers seem to have with flexbox/colspans) and using aria-describedby to map the cells to their related header cells. This seems to work well on NVDA, JAWS and VO. Do you see issues with this approach? https://jsbin.com/yaqulevuci/1/edit?html,output

patrickhlauke commented 3 years ago

confirming that this latest approach from the jsbin appears to be working correctly (though I found some odd behavior in NVDA/chrome when navigating with table navigation (ctrl + alt + cursor keys, when in reading mode), where it just announced the row/column number but not the headers. but following the expected grid navigation itself, rather than table reading mode, the headers were announced correctly in nvda and jaws).