I am working on making an overlay modal more accessible. It works essentially like this JSFiddle. When you open the modal, the focus doesn't properly go into the modal, and it continues to focus on other (hidden, background) items in the page.
You can see in my JSFiddle demo that I have already used aria-controls
, aria-owns
, aria-haspopup
and even aria-flowto
.
<button
aria-controls="two"
aria-owns="true"
aria-haspopup="true"
aria-flowto="two"
onclick="toggleTwo();"
>
TOGGLE DIV #2
</button>
However, while using MacOS VoiceOver, none of these do what I intend (though VoiceOver does respect the aria-hidden
that I set on div two
).
I know that I could manipulate the tabindex
, however, values above 0 are bad for accessibility, so my only other option would be to manually find all focusable elements on the page and set them to tabindex=-1
, which is not feasible on this large, complicated site.
Additionally, I've looked into manually intercepting and controlling tab behavior with Javascript, so that the focus is moved into the popup and wraps back to the top upon exiting the bottom, however, this has interfered with accessibility as well.
Focus can be moved with the focus() method. I've updated the jsFiddle with the intended behavior. I tested this on JAWS on Windows and Chrome.
I've added a
tabindex="-1"
on the "two" div to allow it to be focusable with the focus method.I split the toggle function into two functions, this can probably be refactored to fit your needs, but one function sets the aria-hidden attribute to true and moves the focus on the newly opened modal, and the other function does the reverse.
I removed the excessive aria attributes, the first rule of aria is to only use it when necessary. This can cause unexpected behavior if you're just mashing in aria.
To keep focus within the modal, unfortunately one of the best options is to set all other active elements to
tabindex="-1"
oraria-hidden="true"
. I've applied an alternative where an event listener is added to the last element in the modal upon tabbing. To be compliant, another listener must be added to the first element to move focus to the last element upon a shift+tab event.Unfortunately, to my knowledge there isn't a cleaner answer than those above solutions to keeping focus within a modal.
Use role = "dialog" aria-modal="true" on your modal popup
Make the first and the last focusable element of your modal react on kwydown event, resp. on pressing tab and shift+tab. As far as I ahve tested, it works everywhere.
`Basic example:
Naturally, you need to know what is the first and the last focusable element of your modal. Normally it shouldn't be too complicated. Otherwise if you don't know what are the first and last focusable elements of your modal, it's perhaps a sign that you are making a too complex UI.
aria-disabled
vsaria-hidden
First, note that
aria-hidden
is not intended to be used when the element is visible on the screen:The option you should use is
aria-disabled
tabindex
Removing a link from the tabindex is a WCAG failure if this link is still perceivable from a screenreader or clickable. It has to be used conjointly with
aria-disabled
or better thedisabled
attribute.pointer-events
css propertyThe easiest way to disable mouse events is by using the
pointer-events
css property:The jQuery
:focusable
selecter is the easiest thing you could usesample code