Improve a11y support for table sorting by allejo · Pull Request #1144 · StackExchange/Stacks

I'm really excited by this change! We'll be wanting sortable tables in an internal project and having sorting be keyboard and screen-reader accessible would be a great thing.

Here's another link for reference: https://adrianroselli.com/2021/04/sortable-table-columns.html. It's marked out-of-scope for this PR but this link also presents various different ways to have sortability and sort change to be announced for screen readers. If we do enhance the feature with instructions/announcements here or in future, the HTML will need to provide localised/translated strings for these.


I think we should update the documentation in this PR to ensure devs write in the initial aria-sort attribute for the non-JS table examples where columns are already sorted. (It'd also apply to the JS examples, but those columns aren't pre-sorted in the examples)

Something like this for example:

--- a/docs/product/components/tables.html
+++ b/docs/product/components/tables.html
@@ -634,14 +634,14 @@ description: Tables are used to list all information from a data set. The base s
 <section class="stacks-section">
     {% header "h2", "Sortable tables" %}
     <p class="stacks-copy">To indicate that the user can sort a table by different columns, add the <code class="stacks-code">s-table__sortable</code> class to the table.</p>
-    <p class="stacks-copy">The <code class="stacks-code">&lt;th&gt;</code> cells should include arrows to indicate sortability or the currently applied sorting. In addition, the column that is currently sorted should be indicated with the <code class="stacks-code">is-sorted</code> class on its <code class="stacks-code">&lt;th&gt;</code>.</p>
+    <p class="stacks-copy">The <code class="stacks-code">&lt;th&gt;</code> cells should include arrows to indicate sortability or the currently applied sorting. In addition, the column that is currently sorted should be indicated with the <code class="stacks-code">is-sorted</code> class and the <code class="stacks-code">aria-sort</code> attribute with the <a href="https://www.w3.org/TR/wai-aria-1.1/#aria-sort">appropriate value</a> on its <code class="stacks-code">&lt;th&gt;</code>.</p>
     <div class="stacks-preview">
 {% highlight html %}
 <div class="s-table-container">
     <table class="s-table s-table__sortable">
         <thead>
             <tr>
-                <th scope="col" class="is-sorted">Listing @Svg.ArrowDownSm</th>
+                <th scope="col" class="is-sorted" aria-sort="descending">Listing @Svg.ArrowDownSm</th>
                 <th scope="col">Status @Svg.ArrowUpDownSm</th>
                 <th scope="col">Owner @Svg.ArrowUpDownSm</th>
                 <th scope="col" class="ta-right">Views @Svg.ArrowUpDownSm</th>
@@ -659,7 +659,7 @@ description: Tables are used to list all information from a data set. The base s
                 <table class="s-table s-table__sortable">
                     <thead>
                         <tr>
-                            <th scope="col" class="is-sorted">
+                            <th scope="col" class="is-sorted" aria-sort="descending">
                                 Listing
                                 {% icon "ArrowDownSm" %}
                             </th>

Screen readers can announce the initial state of the sorting even if interactive sorting isn't enabled for the table.


I've noticed that the styling doesn't quite match the previous appearance, especially noticeable in the font using the default <button> font instead of the bolded header font. The cursor is the regular arrow at the moment (instead of cursor) and it's also not possible to select and copy the header text any more:

Screenshot of JS table example with button elements that use default font and can't be selected

I think it just needs some CSS tweaks to get it looking and behaving like before when the text was directly within the <th>. The s-btn__unset class has rules font: unset and user-select: auto that I think fix these. Plus I think cursor: pointer can be added to fix that cursor. Applying these new rules seem to fix things up:

Screenshot of JS table example with button elements that retain font and text selectability of the encompassing TH

(It might need additional tweaking for things I haven't noticed are not identical to before)

I'd love it if the headers had a focus shadow/outline like we see on other elements (right now it's default for button), but I have no idea how to get those showing up without being cropped by the boundaries of the <th> element.


Now that there's a test framework for Stacks, hopefully we can add in tests to verify no breaking changes here (e.g. test ensureHeadersAreClickable keeps working with future changes).

Great work on implementing this! 🥳 Let me know if there's anything I can help with re. testing or anything.