Reordering Lists with aria-owns

Using aria-owns to make list re-ordering accessible.

Background

Imagine you want to create a yet another TODO application. You want to allow your users to re-order their tasks in addition to editing the text.

Re-ordering items using CSS (e.g. using the flex order property) can cause problems for accessibility. The general advice is to ensure that the DOM order of content matches the visual order. This can lead to other problems, which will be addressed later.

By using aria-owns alongside CSS-based reordering, we can ensure screenreaders are able to navigate the content in the appropriate order.

The Problems

  1. Screenreaders, such as NVDA or VoiceOver, generally navigate content in the order it appears in the DOM
  2. Removing an item from the DOM (including reattaching it elsewhere) removes any undo/redo edit history the browser is maintaining

Re-ordering using CSS

If you reorder only using CSS, a screenreader may not navigate the content in the correct order. In the following video, an item is re-ordered within a flex parent container using the CSS order property.

In the above video, I am using Windows Narrator, pressing caps lock + right arrow, to navigate sequentially through the content. Note that after reordering the items, the content is still read in the original order. The item with the text "world" is read as the second list item, rather than the third.

Re-ordering in the DOM

To solve the CSS issue, it can be tempting to instead reorder items by reordering them in the DOM. For example, in React you may re-order an array of items representing your list and use a key for each item when rendering the associated DOM.

This causes two problems:

  • Focus is not properly maintained
  • Any text the user may have entered can no longer be undone

Some DOM rendering libraries attempt to address the former issue by refocusing any element that had focus prior to a re-ordering. However, this may cause a screenreader to read the item again, which can be confusing to the user.

The latter issue is not possible to solve without keeping items untouched within the DOM. The browser maintains a stack of text edits to undo/redo across all <input>, <textarea> and contenteditable elements. The stack is used when undo/redo are invoked via a keyboard shortcut, application menu, document.execCommand or gesture (such as shake to undo on iOS).

Removing an element from the DOM removes any corresponding entries in the stack and adding it back does not restore it. Since inserting an element at a new location first removes it from the DOM, we cannot move the DOM elements if we wish to preserve the stack.

Using aria-owns

The aria-owns attribute allows us to expose a different ordering from the DOM to assistive technologies.

Imagine in the above video, I had used aria-owns to restructure the accessibility tree, with structure looking something like:

<ul aria-owns="item-1 item-3 item-2">
  <li id="item-1" style="order: 1">Hello</li>
  <li id="item-2" style="order: 3">World</li>
  <li id="item-3" style="order: 2">Test</li>
</ul>

With this change, the item with text "Test" will now be the second list item. The browser exposes the desired order to the screenreader and the user is able to properly navigate the content. Since we did not move any DOM elements, any text the user entered can be undone. The below video shows the updated result.

See an example of this in action see the following demo: https://sparhami.github.io/aria-owns-list-reorder/. In the example, you may expect that performing an undo would also undo the reordering, but that is a topic for a future post.

Demo

The following is a live demo of using aria-owns and a flex parent using order to reorder items.

Links: