22

One of the baseline principles of web accessibility is that all functionality of a site should be available via keyboard navigation alone. (That's guideline 2.1 from WCAG 2.1.)

The new comment flag dialog is inaccessible via keyboard navigation.

Keyboard support is important. Many people with muscular difficulties, and basically anyone who's blind and using a screen reader, may be unable to use a mouse and fully reliant on a keyboard.

There's two problems here.

1. The comment flag button cannot be reached by keyboard alone

It's an anchor element (not a <button>) with no href and no tabindex value set. Because of this by default this element is not in tab order and cannot be reached via the tab key. The only way to access that element is by clicking on it.

This also means blind users will be entirely unable to flag comments.

(The same is also true for the comment upvote button, mentioned here: Keyboard-only users cannot upvote/downvote posts or comments.)

2. The comment flag options are not navigable to via keyboard.

If you manage to open the dialog (issue #1 gets fixed and it's in tab order) then you cannot reach the options within it purely via keyboard. Try it, click on a comment flag button:

  • try pressing up or down: the page scrolls.
  • try pressing tab a few times: you'll just tab through links behind the dialog.

There is one way to reach the flag dialog: Eventually if you hold tab for long enough you'll move past every link further along on the page, past the footer, and then reach the comment flag dialog. Whilst that's better than nothing, it's something many non-techy people usually wouldn't even think to try—they'll give up and hit Esc to get rid of the dialog stuck on their screen.

What should happen?

Make the comment flag button (and the upvote button above it) either <button type="button"> or <a tabindex="0" role="button">. ARIA guidelines would suggest use the button element, but if it must stay an anchor element, you want to give it the button role because it isn't a link or anchor, it's a button. (Tabindex of 0 means put it in tab order based on its relative location on the page, i.e., the normal behaviour for anything tabbable.)

I suggest implementing the WAI-ARIA Authoring Practices modal dialog design pattern for the comment flag dialog, as well as the regular post flag dialog (which is still reachable within a tab press or two, but doesn't do all the things it should). It advises you on the ARIA attributes to set and the keyboard interaction pattern users should experience.

7
  • 5
    Here is a comment you can test flagging with. Please don't submit an actual flag using it, just mess around trying to focus the controls inside the flag dialog with only your keyboard. Commented Aug 7, 2018 at 18:29
  • 2
    In my case with the radio buttons, sometimes tabbing or shift-tabbing skips one of them. When "It's no longer needed" is highlighted, hitting tab skips to the Cancel button or when on the Cancel button, hitting shift+tab goes to "Something else", but then pressing shift+tab again skips to "It's unfriendly or unkind". (Chrome OS 67.0.3396.99)
    – user367580
    Commented Aug 8, 2018 at 12:45
  • 1
    @MoonRunestar Yeah, I notice that when I have no radio button selected, tabbing through them will skip some radio buttons inconsistently, which is not correct behaviour. I wonder what's causing that. (And if it's a Chrome bug or something wrong with the HTML.) Commented Aug 8, 2018 at 12:59
  • 1
    I'm not seeing the tabbing behavior you're describing in the dialog. When a radio button is focussed, I can either press arrow keys to make a selection or press tab to exit the radio group and go to the next selectable element: the text area if "Something else" is selected, "Submit" if anything's selected, or "Cancel" if nothing's selected. From the control after the radio buttons, hitting Shift-Tab will take me either to the selected radio button or "Something Else" if unselected. The only surprise I see is that when you select "Something Else", the focus jumps to the textarea. Commented Aug 15, 2018 at 22:33
  • 1
    I've started implementing this, switching from empty <a> tags to <button>. A challenge I'm facing is that just tabbing through things absent of any other accessibility technology doesn't appear to advance the focus from the button to the dialog even with tabindex, role=dialog and aria-modal=true in play. I think in this specific case, I would need to explicitly focus something a control when the dialog appears. Commented Aug 15, 2018 at 22:38
  • 1
    @Brian Thank you for working on this. :) Yes, you'll need to explicitly set focus on something using JS. Typically the same code that opens a dialog will also put focus on the earliest focusable thing inside it, or look for an element in the dialog with something like a js-focus-first class and put focus on that. For the report dialogs that would (should) be the radio buttons. Note you'll also need to implement a tab trap within the dialog to keep focus from moving out of it. (google "accessible dialog tab trap") When the dialog closes you'll put focus back onto the button that opened it. Commented Aug 15, 2018 at 22:50
  • This change (for comments at least) is in the code review queue and it looks pretty much exactly like you described. 🙂 Thanks for the detailed bug report. Commented Aug 16, 2018 at 22:02

1 Answer 1

8

This is in production.

The fix is pretty much exactly what you described in the question and comments, so thanks for that. 🙂

Inside comments

The comment upvote and flag buttons are now <button>s, meaning they are tab-able and have some subtle styling which is really apparent on focus. They are also visible to screen readers as a result.

In the dialog

There are now three special classes in the dialog:

  • .js-modal-initial-focus. The first one of these in the dialog gets auto-focused when the dialog appears.
  • .js-first-tabbable. This element is a rejects shift tab key presses and is at the beginning if tab ordering. For the purposes of this dialog, I actually had to add it and tabindex=0 to the title because tab ordering can happen all sorts of different ways with radio buttons.
  • .js-last-tabbable. The same as above, but for tab. This is placed on the close button in the top right corner.

When the dialog closes

There are a few caveats here. If...

  • the dialog closes naturally[1] with Esc, clicking outside the dialog, clicking Cancel or X, or on submission,
  • and the dialog was opened via the keyboard (the button is focussed and the screen coordinates of the click are (0, 0)),

... focus will return to the first of the following elements that exists on the page:

  1. The button that opened the dialog (if we cancel or flagging didn't delete the comment)
  2. The flag button on the comment below it (if there is one and the comment was deleted)
  3. Otherwise, the "add a comment" or "show more comments" on that comment section (if they exist).

What's missing?

It looks like a few parts aren't working like I was hoping on Safari so I'm gonna go back at look at that. I also need to validate VoiceOver behavior now that I'm in a good place to test it.

What about Question/Answer flagging?

That is a much bigger project than question flagging because question flagging has multi-page dialogs. E.g. Flag -> Close -> Close as Duplicate. Keyboard selection is also hard, because as soon as you select "Close" it navigates to a new dialog, so arrowing through radio buttons wouldn't work out of the box.

It would need a major design overhaul to even get started. I encourage you to open it as a separate issue (if it doesn't already exist) and I'll bubble it up to the Welcome Wagon team for prioritization. The good news is the work done here can be universally applied to any of our new .s-modal dialogs.


[1] An unnatural close is if something happens like another dialog forcibly closing it.

5
  • 1
    Thank you. Excellent work. One thing is missing: the flag and upvote buttons have no text equivalent for screen readers. (They have title attributes, but screen readers ignore that attribute almost always.) The upvote button should have aria-label="Upvote this comment as useful" and the flag dialog should have something like aria-label="Flag this comment for moderator attention". A different method is add an element inside the button such as <span class="sr-only">Upvote this comment as useful</span> using an sr-only class. (Don't use both methods.) Commented Aug 18, 2018 at 15:28
  • 1
    I've opened a follow-up report as you requested here: Keyboard-only users cannot flag questions or answers Commented Aug 18, 2018 at 16:22
  • 1
    Cool. I've handed that over to our PM for review. I didn't realize title wasn't universally supported; the only screen reader I test with is VoiceOver and it picked up on the text. Do you have a go-to resource for browser accessibility details? I've been trying to look things up, but I'm just cobbling together knowledge from various blog post. Commented Aug 20, 2018 at 23:28
  • 2
    Cobbling together knowledge from various sources is kinda how the accessibility world looks at the moment. My most notable sources have been: WCAG 2.1 (if you dive into a criterion's "understanding" or "how to meet" pages, including the techniques further down in the “understanding” page—see the links on the right—things get very detailed), ARIA and its design patterns, WebAIM's articles, and Paciello Group's developer blog. Commented Aug 22, 2018 at 10:32
  • 1
    For the title attribute specifically: I thought title text improved accessibility. I was wrong. is a good explanation. :) Right now only some screen readers may ever pay attention to a title attribute, and usually only for a block level element with no content (e.g. an empty div with a title attribute). In general, assume a title attribute will never get read out, and give the element another kind of text alternative: some sr-only text, an aria-label, alt text for an image, etc. Commented Aug 22, 2018 at 10:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .