I have implemented a popup that is triggered from a button on my webpage. This popup nearly fills the entire screen and prevents users from interacting with anything on the webpage until the popup is dismissed. This works well for sighted users, but when using VoiceOver on the Mac you can still navigate the underlying web page content, and the blind user would have no idea a popup window was presented.
How can I prevent VoiceOver from navigating every element on the page except for one div (and every element in that div)?
I know one can use aria-hidden="true" to hide it from screen readers, and I know one can force a focus on an element, but I'm not sure how best to accomplish this. Do I need to loop over the entire DOM and essentially hide everything then upon close unhide everything? Or is there a better approach, some ARIA property that defines this type of element that's presented perhaps?
A website that exhibits the desired behavior is Piazza. When you activate the Login button it presents a popup modal dialog and demands focus, and you can't navigate away from it until you dismiss the popup.
Implementing accessible modal dialogs correctly has got to be one of the trickier aspects of web accessibility, since there's just so many things to watch out for, on top of the usual issues of screenreader and browser compat and adherence to spec. Here's a summary(!), somewhat based on personal experience, this article from Smashing Magazine on accessible Modal dialogs, WAI-ARIA 1.0 Authoring Best Practices and this slideshow on the same topic.
(Note that you'll find some conflicting advice between these; this is somewhat due to differences in behavior between screenreaders/browsers and spec, and different authors having different positions on whether to stick to spec vs work with the screenreaders that real users actually use.)
Tell screenreaders it's a dialog: Add role="dialog" to the parent DIV, and set aria-labelledby to point to the ID of the title.
This causes the screenreader to recognize the piece of UI as a dialog in the first place; when the user navigates into it, either by navigating or because focus moves into it, the screenreader will announce that the user is now in a dialog, and will likely also announce the title. (On some screenreader/browser combinations - notably VoiceOver+Safari, it may also affect how screenreader navigation works.) This alone does not 'hide' any of the other UI on the page, however.
Add basic keyboard support
There's a bunch of things to do here that are important for both screenreader users and non-screenreader using keyboard users. Two key issues here are to move focus to somewhere appropriate in the the dialog when it initially appears, and when the dialog is dismissed, restore focus back to where it was when the dialog originally appeared.
Make it modal for screenreader users and keyboard users
Dialogs come in two flavors: modal, which don't let the user interact with any other UI while present, and modeless, which do let the user leave the dialog and return later - examples might be the "Find Text" dialogs in some text editors. role="dialog" doesn't distinguish between these two cases, and also using ARIA doesn't have any affect on browser behavior, so if your dialog is modal, you have to do extra work yourself to make it behave as modal. There's two aspects to this:
Just as modal dialogs use a gray scrim or blur effect to visually hide the inactive background from sighted users, we also want to hide this content from screenreader users: otherwise they will still be able to navigate out of the dialog to the background, or use other hotkeys to list links or headings or other controls from the page and will see both items from the background part of the page and the dialog. In the past, putting aria-hidden="true" on all other top-level DIVs was the best tool to use to do this (and a bit fiddly - remember to remove it when done!), but as of 2020, aria-modal="true" appears to be decently supported and is much easier to implement.
For both screenreader users and non-screenreader-using keyboard users, you also need to make the keyboard behavior modal: hitting tab from the last item in the dialog should wrap to the top of the dialog, and the inverse behavior for shift-tab. If you don't do this, keyboard - and screenreader - users can simply tab right out of your dialog into the background of the page. There's different approaches for doing this, most involve tracking focusin or similar at the body level, and forcing focus back into the dialog if it "escapes".
Other tweaks/fixes/hacks
Windows-based screenreaders such as NVDA and JAWS go into "application mode" when they enter a dialog: their web navigation hotkeys no longer work, and they treat the content of the dialog as a form rather than a rich web page. This is fine is the dialog content is a classic form with just fields, labels and buttons, but isn't a good fit if the dialog contains web content with text, hyperlinks, and the like. In that case, you might want to place a <div role="document">...</div> within your role="dialog" but wrapping the main content.
Ensure dialog content is itself accessible
Should go without saying, but all of the above counts for nothing unless the content within the dialog is itself accessible.
Most importantly: understand why you are making the dialog accessible in the first place, and test appropriately.
Unfortunately, at the moment (Jan 2015!) there's still a lot of variation in behavior between different screenreaders (also depending in browser used); it's almost like how browsers were with CSS a decade(!) or so ago. It's worth understanding why you want to make your UI accessible, figure out where most of your users are going to be, and test appropriately given the screenreaders they'll be using. From the most recent (Jan 2014) WebAim Screenreader survey, Windows readers - notably JAWS and NVDA - have the majority of the market, with VoiceOver coming third.
Here's one possible strategy to consider:
If you just want to do a basic 'due diligence', then testing with just VoiceOver may be fine. The experience may not be as good on the other screenreaders, but if something works with VO, it's likely not going to be completely broken on the others.
Next level up is to get a copy of (free, but feel free to donate) NVDA windows-based screenreader and run it in a VM on a Mac (assuming you're using a Mac here). NVDA and JAWS tend to be pretty close behavior-wise.
If accessibility is part of your job description, then likely you or your company will need to get a copy of JAWS ($800+!), since that appears to be the screenreader of choice for government and educational institutions. (This is perhaps the accessibility analog of testing with downlevel versions of IE!)
You should iterate through all the top level items and set aria-hidden=true on those items. This is an example:
<!doctype html>
<html>
<head>
<title>aria-hidden examples</title>
</head>
<body>
<a aria-hidden="true" href="#">Link at top</a>
<p aria-hidden="true">Lots of text content</p>
<form aria-hidden="true">
<label for="input">Label</label>
<input id="input" type="text" />
<button>Submit</button>
</form>
<div>
<label for="dialogInput">Nother Label</label>
<input id="dialogInput" type="text"/>
</div>
<form aria-hidden="true">
<label for="input2">Label 2</label>
<input id="input2" type="text" />
<button>Submit 2</button>
</form>
<a aria-hidden="true" href="#">Link at bottom</a>
</body>
</html>
The problem is that this will cause the announcements to stay within the "dialog", but if you press the tab key, the focus can move outside the dialog and not get announced. This means that if there are interactive elements outside the dialog, the user could end up interacting with the wrong elements. Other browsers will do similar things.
So you will need to trap the focus inside the dialog using a Javascript event handler to handle your TAB keys if your page includes other naturally focusable elements like in the example above.
WAI-ARIA provides the dialog and alertdialog roles to define dialogs. The dialog role is used when the user is expected to provide data, and the alertdialog role is used to announce the contents of a dialog to the user.
Alert dialog
<div role="alertdialog"
aria-labelledby="dlgtitle"
aria-describedby="instructions">
<h1 id="dlgtitle">Shutdown instructions</h1>
<ol id="instructions">
<li>Open timesheet</li>
<li>Enter time for today</li>
<li>Close all open applications</li>
<li>Shut down system</li>
</ol>
<div>
<input type="button" value="OK">
</div>
</div>
Dialog
<div role="dialog" aria-labelledby="dlgtitle">
<h1 id="dlgtitle">Sign up to Newsletter</h1>
<ol id="instructions">
<li>Enter email address</li>
<li>Press the 'Sign up' button</li>
<li>You're all signed up!</li>
</ol>
<div>
<label for="email">Email: </label>
<input type="text"
id="email"
name="email"
aria-describedby="instructions">
<input type="button" value="Sign up">
</div>
</div>
WAI-ARIA Authoring practice design pattern for a modal dialog
Related
I am working on react based application for mobile screen readers. Use case is that for a dialog with menu items, I have to keep one button to dismiss the dialog, which will be on top of the dialog. I have to set it's tabIndex to 0 to make it accessible, which results in dismiss button being the first focusable item.
Expectation is that after screen reader lands focus on first menu item, the dismiss button should be accessible. How to approach this problem?
I have tried the following:
<Dialog open={this.state.menuOpen} className="hide-default-dialog-container"
content={<div className="actionsheet-view-bg" onClick={() => {
this.setState({menuOpen: !this.state.menuOpen})
}}> {this.getDismissButtonForActionSheet()}
<div className="actionsheet-view-container"> {this.getActionSheetItems()} </div>
</div>}
/>
You have four options here:-
Option 1. Move the close button to the end of the dialog next to any save / update buttons.
The 'x' in the top right of dialog boxes is expected, but there is no reason why you can't add a close button at the bottom of your dialog instead, placed after any save / update buttons.
So in the footer of your dialog have
<button>Save</button> <button>Cancel</button>
You could still maintain the 'x' in the top right but use aria-hidden="true" on it without a tabindex.
Option 2. Change the button to be after the content in the DOM but use absolute positioning to place it visually
You could move the close button after the content (in the DOM) and then use CSS to position it in the top right of the dialog.
This is a bit of a grey area on whether it would constitute a logical tab order but I think it is still accessible enough to be a good pass.
Option 3. Manage the focus programatically.
When the dialog opens you could just programatically focus the first item. Just make sure you trap focus within your dialog so the close button can be reached.
Option 4. Do nothing
This is the best option
There is nothing wrong with the close button being the first focusable item (in fact it may be preferential to allow users who accidentally open the dialog to close it quickly).
Screen reader users are used to exploring dialogs and so it won't impact them if the first focusable item is the close button. It may even be reassuring to know that you have provided an accessible close button (as many developers forget about keyboard users and make buttons inaccessible to close dialogs).
Semantics, Escape and Focus Management.
To further increase the accessibility change your close div into a button as that way you don't have to worry about adding tabindex as it is semantically correct. (I have assumed the close button is a div as you said you added a tabindex="0" - if it is already a button then the tabindex is not needed)
Secondly make sure the dialog can be closed using the Escape key as that is expected behaviour.
Finally ensure focus remains confined to the modal (not just via the tab key, you need to add aria-hidden="true" to all the content outside of the dialog while it is open as screen reader users navigate with more than just the tab key, in fact it is a secondary way of navigating, screen reader users navigate by headings 1-6, links, forms, buttons etc.).
This may require structuring your page to make it easier
For example:-
<header aria-hidden="false"></header>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<dialog aria-hidden="true"></dialog>
would turn into the following when the dialog is open (and reverted when the dialog is closed).
<header aria-hidden="true"></header>
<main aria-hidden="true"></main>
<footer aria-hidden="true"></footer>
<dialog aria-hidden="false"></dialog>
$('#firstButton').focus();
<div id="liveRegion" aria-live="assertive">
This is a very long text
</div>
<button id="firstButton">First Button</button>
I am quite a newbie in accessibility issues. I basically have a button on which I want the focus to be on page load, but I also want a text to be picked up by the screenreaders in the liveRegion.
Right now, the text will be inteerrupted at some point during page load, and focus goes to the button.
Is there a way to have the screenreader not be interrupted?
Live regions can be confusing at first. The purpose of a live region is to announce changes to the page, not to announce page load information. After your page is loaded, if something changes on the page, such as the text within an element, or a new element is added, those changes can be announced if the aria-live attribute is used.
Most pages do not announce anything as they're loaded. A screen reader user will hear that the page is loading, and when it's done (usually there's an audible clue in the screen reader to let you know if the page is still loading or if it's done), the page title is typically announced and then whatever object has initial focus is read.
If you really need something read after the page loads, then it should probably have the initial focus. But be careful because putting focus on a non-interactive element such as a paragraph (<p>) or non-semantic elment (<div>) can be confusing. If you have to resort to that, make sure that element has tabindex="-1" so that the element will not be in normal keyboard focus order.
as such, there is no way to prevent the reading of your text by the button gaining focus.
The order of priority of what to say when heavily depends on screen reader / browser / OS combination, but usually, an element taking the focus has priority against any live region, including assertive.
What you can do is making the live region assertive text appear after the focus is moved to the button.
It has greater chances to be announced without interruption then.
IN any case, a live region present at page load isn't reliably read on all platforms.
To make sure that it will be effectively spoken, make it appear in the DOM after the page has completely finished to load.
I have a form page that, while tabbing through near the end, rather than going to the next input field focus jumps to IE's own selectables (url bar, tabs, etc), and won't go back to page elements at all. It only cycles around the browser options. Even clicking an earlier input field to focus that then hitting tab once puts you back in the url bar.
This is a weird one, no amount of searching has found a single other person mentioning similar. It appears to be completely EI's fault, but I have to deal with it somehow. The site I develop for has to be completely keyboard accessible per the WCAG2.0, and this is a hard stop currently.
I've tested it quite a bit and found a bit of info, though the specifics I've found only make it stranger.
It seems to be related to the fixed footer IE adds to deliver notes about saved form info or partial loading and such. For one, if such footer doesn't appear at all, the page works fine. It there is the footer, when the tab jumps out it will land there if it still exists; if it has been closed by clicking then the jump will be to the url. Here is the interesting bit though- if you tab into it and close it by hitting enter with focus over the x, then the page works perfectly after that! The next tab after hitting close puts focus back where it was, and it then continues as normally expected. It has to be closed by keyboard though, closing with mouse leave the page totally focus-cursed unless you refresh.
The jump out point is always the same tabbing down, but if you click focus below that and shift+tab up it does still jump out, but between a higher point. With form elements wxyz, it is between y and z going down, but between y and x going up, so in either case it is upon leaving x.
Any ideas at all?
Edit2: I've been cleared to link the site. It occurs on South Moon Under's billing page. To get there, you have to put something in the cart, hit (guest) checkout, and fill+click passed the shipping page (dumby info is fine for this, Mr. Test). On the billing page nothing needs to be filled to see the issue, just tab down; the jump is between the giftcard number and pin fields. (reminder: only occurs if IE's footer appeared, and is worst if you closed it by mouse.)
Edit: The form is totally normal HTLM, and works fine in all other cases and browsers. I can post the code but it isn't interesting.
<div class="formfield giftcertfield">
<label class="label" for="dwfrm_billinggiftcert_giftCertCode">
<span class="requiredindicator">*</span>
<span class="labeltext">Redeem Gift Card:</span>
</label><!-- END: label -->
<div class="value">
<input class="textinput required" id="dwfrm_billinggiftcert_giftCertCode" type="text" name="dwfrm_billinggiftcert_giftCertCode" value="" maxlength="2147483647">
</div>
<div class="clear"></div>
<br>
<label class="label" for="dwfrm_billinggiftcert_giftCertCodePin">
<span class="requiredindicator">*</span>
<span class="labeltext">Pin Number:</span>
</label><!-- END: label -->
<div class="value">
<input class="textinput required" id="dwfrm_billinggiftcert_giftCertCodePin" type="text" name="dwfrm_billinggiftcert_giftCertCodePin" value="" maxlength="2147483647">
</div>
...And then a couple more buttons and such. The jump happens between these two inputs though (going down, it is between the first here and a checkbox in a different form going up.)
I'm developing a web application that needs to be accessible via JAWS. There is a requirement which says that newly opened dialogs need to be focused on in order to be announced by JAWS. I implemented this, and it's working fine without JAWS: the dialog's first element has focus. Testing this web site with JAWS+IE shows that the element is focused, but the braille viewer shows something different (part of the page header) and that's also being read.
From my understanding, this means that the PC's cursor is correct, but the JAWS cursor is misplaced.
Is there any way to influence the JAWS cursor placement using JavaScript?
When testing with JAWS, you need to not just look at the screen but listen to what JAWS is saying. Spend time getting to know JAWS intimately if you need your testing to provide reliable results.
The best way to create an accessible dialog is to:
Ensure the dialog heading has an ID attribute.
If the dialog has a short message, make sure this also has an ID attribute.
Give the container a role of "dialog" and a tabindex of "-1".
Ensure that the container's ARIA-LABELLEDBY attribute contains the ID of the heading.
Ensure the container's ARIA-DESCRIBEDBY attribute contains the ID of the short message if there is one.
When the dialog opens, send focus to the container.
If you follow this pattern to the letter, your dialog will work beautifully with JAWS and other AT.
Simple example:
<div id="dlg_discard" tabindex="-1" role="dialog" aria-labelledby="dialog_heading" aria-describedby="dialog_message">
<h1 id="dialog_heading">Discard changes?</h1>
<p id="dialog_message">Are you sure you want to discard your changes?</p>
<button>Yes</button> <button>No</button>
</div>
... And the jQuery to make the dialog visible ...
$("#dlg_discard").show().focus();
Is there any way to influence he JAWS cursor placement (via Javascript)?
No
I'm working on a project with Twitter Bootstrap and playing around the JavaScript components using a screen reader.
When I trigger the modal dialog, Jaws skips the modal going to the next link in the page.
Is there a way to implement a accessible modal?
Another solution that I think is to make a static page to the functionality of the modal, and redirect to this page when the user use a screen reader. Can I detected somehow if the user are using a screen reader?
EDIT 2019: N. Hoffmann wrote and maintains an accessible modal component both in vanilla JS (along other components in its van11y project) and jQuery.
Behavior and styles are easily modified via data-* attributes and classes.
It's been tested in way more conditions (screen readers, etc) that what you'd do with your own script ;-)
Also Bootstrap 4 has a fairly accessible modal and Bootstrap 3 in its latest versions (much or all of the Paypal Bootstrap accessibility plugin was backported to 3.3.x).
Modern ressources: Access & Use european initiative details a lot of interesting aspects in a simple manner and points to other resources, including the latest ARIA Deisgn Pattern.
Here's an accessible modal dialog: http://hanshillen.github.com/jqtest/#goto_dialog
Once the modal is activated, keyboard navigation is trapped inside the dialog till it's explicitly closed by the user.
http://irama.org/web/dhtml/lightbox/ details such an accessible implementation (there's little difference between a lightbox and a modal dialog, the important thing is the modal part and keyboard management).
You can also read in Unofficial copy of the DHTML Style Guide the dialog modal part and W3C/WAI-ARIA Making a Dialog Modal.
J. Wajsberg wrote a jQuery plugin able to trap the keyboard input inside a DOM element if you need a more DIY approach.
i don't know any solution to detected automatically if user using a screen reader. but there is the google solution to hide a link at the begining of the page (with left:-1000em and position:absolute) that can be activate if you use keyboard and display a "special mode".
Screen reader users, click here to turn off Google Instant.
for your modal dialog try to use aria and aria-atomic="true" aria-live="assertive" attribute on the dialog html div. it should announce the content of you dialog box.