[upstream] [Table] CKEditor 5 changes <thead> and keeps only a single <tbody>, which impedes screen reader accessibility of HTML tables

Created on 30 August 2023, 9 months ago
Updated 14 February 2024, 4 months ago

Problem/Motivation

CKEditor mangles the following tables in various ways, causing data loss and severe accessibility issues.

Table 1

This table has a rowgroup header with a colspan

<table>
    <thead>
    <tr>
        <th scope="col">Col Header 1</th>
        <th scope="col">Col Header 2</th>
        <th scope="col">Col Header 3</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="rowgroup" colspan="3">Rowgroup Header 1</th>
    </tr>
    <tr>
        <th scope="row">Row Header 1</th>
        <td>Data 1,1</td>
        <td>Data 2,1</td>
    </tr>
    </tbody>
</table>

Table 2

This table has two rowgroup headers that have colspans.

<table>
    <thead>
    <tr>
        <th scope="col">Col Header 1</th>
        <th scope="col">Col Header 2</th>
        <th scope="col">Col Header 3</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="rowgroup" colspan="3">Rowgroup Header 1</th>
    </tr>
    <tr>
        <th scope="row">Row Header 1</th>
        <td>Data 1,1</td>
        <td>Data 2,1</td>
    </tr>
    </tbody>
    <tbody>
    <tr>
        <th scope="rowgroup" colspan="3">Rowgroup Header 2</th>
    </tr>
    <tr>
        <th scope="row">Row Header 2</th>
        <td>Data 1,2</td>
        <td>Data 2,2</td>
    </tr>
    <tr>
        <th scope="row">Row Header 3</th>
        <td>Data 1,3</td>
        <td>Data 2,3</td>
    </tr>
    </tbody>
</table>

Table 3

Similar to previous examples, but we've removed the colspan on the rowgroup and replaced with empty td elements. We've also added some row headers to the second column and a tbody block that doesn't have any th with a scope set to rowgroup.

<table>
    <thead>
    <tr>
        <th scope="col">Col Header 1</th>
        <th scope="col">Col Header 2</th>
        <th scope="col">Col Header 3</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="rowgroup">Rowgroup Header 1</th>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <th scope="row">Row Header 1</th>
        <th scope="row">Row Subheader 1</th>
        <td>Data 1,1</td>
    </tr>
    </tbody>
    <tbody>
    <tr>
        <th scope="row">Row Header 2</th>
        <th scope="row">Row Subheader 2</th>
        <td>Data 1,2</td>
    </tr>
    <tr>
        <th scope="row">Row Header 3</th>
        <th scope="row">Row Subheader 3</th>
        <td>Data 1,3</td>
    </tr>
    </tbody>
    <tbody>
    <tr>
        <th scope="rowgroup">Rowgroup Header 2</th>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <th scope="row">Row Header 4</th>
        <th scope="row">Row Subheader 4</th>
        <td>Data 1,4</td>
    </tr>
    </tbody>
</table>

Table 4

Just for fun, let's try an example table from the HTML5 spec.

<table>
    <colgroup> <col>
    <colgroup> <col> <col> <col>
    <thead>
    <tr> <th> <th>2008 <th>2007 <th>2006
    <tbody>
    <tr> <th scope=rowgroup> Research and development
        <td> $ 1,109 <td> $ 782 <td> $ 712
    <tr> <th scope=row> Percentage of net sales
        <td> 3.4% <td> 3.3% <td> 3.7%
    <tbody>
    <tr> <th scope=rowgroup> Selling, general, and administrative
        <td> $ 3,761 <td> $ 2,963 <td> $ 2,433
    <tr> <th scope=row> Percentage of net sales
        <td> 11.6% <td> 12.3% <td> 12.6%
</table>

Steps to reproduce

Paste the above code into the source view of the CKEditor reference implementation. Click off source view and then back into it. CKEditor produces the following...

CKEditor has moved the thead element to below the parent row for "Rowgroup Header 1", which makes the table inaccessible to screen readers.

<table>
    <thead>
    <tr>
        <th scope="col">
            Col Header 1
        </th>
        <th scope="col">
            Col Header 2
        </th>
        <th scope="col">
            Col Header 3
        </th>
    </tr>
    <tr>
        <th colspan="3" scope="rowgroup">
            Rowgroup Header 1
        </th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="row">
            Row Header 1
        </th>
        <td>
            Data 1,1
        </td>
        <td>
            Data 2,1
        </td>
    </tr>
    </tbody>
</table>

CKEditor has moved all the parent rows for rowgroup headers into the thead element. It has also put all the rows after the thead block into one tbody block. This makes the table inaccessible to screen readers and is a loss of data, since we can no longer reconstruct the original table due to loss of the tbody blocks.

<table>
    <thead>
    <tr>
        <th scope="col">
            Col Header 1
        </th>
        <th scope="col">
            Col Header 2
        </th>
        <th scope="col">
            Col Header 3
        </th>
    </tr>
    <tr>
        <th colspan="3" scope="rowgroup">
            Rowgroup Header 1
        </th>
    </tr>
    <tr>
        <th colspan="3" scope="rowgroup">
            Rowgroup Header 2
        </th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="row">
            Row Header 1
        </th>
        <td>
            Data 1,1
        </td>
        <td>
            Data 2,1
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 2
        </th>
        <td>
            Data 1,2
        </td>
        <td>
            Data 2,2
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 3
        </th>
        <td>
            Data 1,3
        </td>
        <td>
            Data 2,3
        </td>
    </tr>
    </tbody>
</table>

CKEditor has not moved the all the parent rows for rowgroup headers into the thead element, but has put all the rows after the thead block into one tbody block. This causes data loss, since we can no longer reconstruct the original table due to loss of the tbody blocks. And this makes the table inaccessible to screen readers. CKEditor has also turned row headers in the second column to td elements with a scope attribute, which is invalid, and causes further accessibility issues.

<table>
    <thead>
    <tr>
        <th scope="col">
            Col Header 1
        </th>
        <th scope="col">
            Col Header 2
        </th>
        <th scope="col">
            Col Header 3
        </th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="rowgroup">
            Rowgroup Header 1
        </th>
        <td>
            &nbsp;
        </td>
        <td>
            &nbsp;
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 1
        </th>
        <td scope="row">
            Row Subheader 1
        </td>
        <td>
            Data 1,1
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 2
        </th>
        <td scope="row">
            Row Subheader 2
        </td>
        <td>
            Data 1,2
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 3
        </th>
        <td scope="row">
            Row Subheader 3
        </td>
        <td>
            Data 1,3
        </td>
    </tr>
    <tr>
        <th scope="rowgroup">
            Rowgroup Header 2
        </th>
        <td>
            &nbsp;
        </td>
        <td>
            &nbsp;
        </td>
    </tr>
    <tr>
        <th scope="row">
            Row Header 4
        </th>
        <td scope="row">
            Row Subheader 4
        </td>
        <td>
            Data 1,4
        </td>
    </tr>
    </tbody>
</table>

CKEditor has added inline styles to col elements (what?), put all the rows after the thead block into one tbody block (again, potential data loss and makes the table inaccessible to screen readers). I'm not sure what the practical implications of this next quirk are, but I want to note it. CKEditor has added a non-breaking space character to an empty th tag. I don't think non-breaking spaces are ASCII whitespace. This means that the header cell is non-empty and user agents (including screen readers) should add it to the header list for the cells beneath it, according to the spec.

<table class="ck-table-resized">
    <colgroup><col style="width:25%;"><col style="width:25%;"><col style="width:25%;"><col style="width:25%;"></colgroup><colgroup><col><col><col></colgroup>
    <thead>
    <tr>
        <th>
            &nbsp;
        </th>
        <th>
            2008
        </th>
        <th>
            2007
        </th>
        <th>
            2006
        </th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <th scope="rowgroup">
            Research and development
        </th>
        <td>
            $ 1,109
        </td>
        <td>
            $ 782
        </td>
        <td>
            $ 712
        </td>
    </tr>
    <tr>
        <th scope="row">
            Percentage of net sales
        </th>
        <td>
            3.4%
        </td>
        <td>
            3.3%
        </td>
        <td>
            3.7%
        </td>
    </tr>
    <tr>
        <th scope="rowgroup">
            Selling, general, and administrative
        </th>
        <td>
            $ 3,761
        </td>
        <td>
            $ 2,963
        </td>
        <td>
            $ 2,433
        </td>
    </tr>
    <tr>
        <th scope="row">
            Percentage of net sales
        </th>
        <td>
            11.6%
        </td>
        <td>
            12.3%
        </td>
        <td>
            12.6%
        </td>
    </tr>
    </tbody>
</table>
šŸ› Bug report
Status

Postponed

Version

11.0 šŸ”„

Component
CKEditor 5Ā  ā†’

Last updated 1 day ago

Created by

šŸ‡ŗšŸ‡øUnited States jameslp

Live updates comments and jobs are added and updated live.
  • Accessibility

    It affects the ability of people with disabilities or special needs (such as blindness or color-blindness) to use Drupal.

  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024