Settings page keyboard navigation#8577
Conversation
DNin01
left a comment
There was a problem hiding this comment.
Did some testing with Windows Narrator—there's still room for improvement with screen reader navigation, but, with a keyboard, things are at least usable now.
Some screen reader problems:
- Many icon buttons don't have a name
- Addon setting elements aren't linked to their labels
- Item lists like the category panel don't function like lists
Some keyboard navigation problems:
- Setting tooltips can't be accessed
|
I added keyboard navigation to dropdowns by simply setting The button that opens the dropdown still needs to have an |
|
The correct keyboard behavior for dropdowns is that up and down arrows focus the previous/next item and Tab focuses the next element outside the dropdown. |
I have before made a library with this exact functionality, so I'll look into this. |
|
I have not done any screen reader testing myself yet.
I was hoping not to add another libary but it might be worth it if it needs more than a few lines of JS. |
|
Blockly's menu implementation might be useful as a reference: https://github.com/google/blockly/blob/139fa2b/core/menu.ts#L407-L462 |
Enter doesn't trigger @click on li elements, also close dropdown on escape.
Maybe I should just tell you what I have in store...const shiftAmountsByKey = {
ArrowUp: -1,
ArrowDown: 1,
ArrowLeft: -1,
ArrowRight: 1,
Home: -1000,
End: 1000,
};
/** Makes a list-like element arrow-key navigated. */
export default class ArrowKeyList {
constructor(element) {
element.addEventListener("keydown", (e) => {
if (e.ctrlKey || e.metaKey || e.altKey) return;
if (e.key === "Tab") {
if (e.shiftKey) {
// Focus the first child, then the default behavior of Shift+Tab will move to the previous element
element.firstElementChild.focus();
} else {
// Focus the last child, then the default behavior of Tab will move to the next element
element.lastElementChild.focus();
}
} else {
const shiftBy = shiftAmountsByKey[e.key];
if (shiftBy) e.preventDefault(); else return;
const oldFocusIndex = Array.from(element.children).indexOf(document.activeElement);
// Move,
const adjustedFocusIndex = oldFocusIndex + shiftBy;
// clamp,
const newFocusIndex = Math.min(Math.max(adjustedFocusIndex, 0), element.childElementCount - 1);
// set focus!
const targetElement = element.children[newFocusIndex];
targetElement.focus();
}
});
}
}I'm not familiar with Vue at all, so I don't know how to work this code (which has been stripped down, by the way) into our project. (If you want to figure it out, feel free to use this snippet under the same license terms of our project.) I think it's more important that we add ARIA attributes to our menus, though. Or, I wonder if there's a library that handles ARIA attributes and navigation for us. |
Thanks @DNin01 for the code snippet!
|
Menu arrow key naviagtion works now. I'm not sure if the menus should stay open when tabbing away, currently they doesn't.
Just to be clear you mean this repository's license, right? |
|
It works! The dropdowns, menus, and menu items are announced as such:
However, icon-buttons still need readable labels—currently most of the images don't have alt text, and when there's no text, the default name is just "graphic" or "button".
On GitHub, they collapse when you tab outside.
Correct. |
I managed to get this working, but it relies on the addon cards' style attributes being an exact value (to skip over addons in collapsed groups).
<template>
- <div class="addon-body" v-show="shouldShow" :id="'addon-' + addon._addonId">
+ <div
+ class="addon-body"
+ v-show="shouldShow"
+ :id="'addon-' + addon._addonId"
+ tabindex="0"
+ @keydown="handleKeys"
+ >
<div class="addon-topbar">
},
+ handleKeys(e) {
+ if (!e.target.classList.contains("addon-body")) return;
+ if (e.ctrlKey || e.metaKey || e.altKey) return;
+ const cards = document.querySelectorAll(".addons-container .addon-body:not([style='display: none;'])");
+ const index = Array.prototype.findIndex.call(cards, (i) => i === document.activeElement);
+ if (e.key === "ArrowUp") {
+ if (!cards[index - 1]) return;
+ e.preventDefault();
+ cards[index - 1].focus();
+ } else if (e.key === "ArrowDown") {
+ if (!cards[index + 1]) return;
+ e.preventDefault();
+ cards[index + 1].focus();
+ }
+ },
toggleAddonRequest(event) {There's probably a better way to do this. Maybe we could revisit it another time. |
This comment was marked as resolved.
This comment was marked as resolved.
Not sure if this is any better than just manully handling keypresses
- Fix borders - Fix Windows Narrator cursor - Designate parent element as a radiogroup
That's even better! The options are announced as radio buttons by the screen reader. |
a1779f7 to
78c6139
Compare
This comment was marked as resolved.
This comment was marked as resolved.
c0299ec to
dfec573
Compare
dfec573 to
be5f450
Compare
This comment was marked as resolved.
This comment was marked as resolved.
|
Now that this is done, I'll try to work on improving screen reader support and some other more specific UI interactions. |
Resolves #2532
Changes
<dialog>elementaddon-group,btn-dropdownandback-buttoninto a single classAlways shows the dismiss admin message link (regular comments are unaffected)Reason for changes
It's good for accessibility and not being able to toggle addons with the keyboard is annoying.
Tests
Tested on Chromium
Todo: