I am using the javascript library vex to output dialogs like this
When I used this with a screen reader (narrator), that text is not read so I tried to change the javascript inside vex that manipulates the DOM.
The resulting DOM became like this
<div class="vex vex-theme-default" role="dialog">
<div class="vex-content">
<form class="vex-dialog-form">
<div class="vex-dialog-message" tab-index="0"
aria-label="Login failed">Login failed!</div>
<div class="vex-dialog-input"></div>
<div class="vex-dialog-buttons">
<button type="submit"
class="vex-dialog-button-primary btn vex-dialog-button vex-first">Ok</button>
</div>
</form>
</div>
</div>
but the message "Login failed" is not read even with this markup.
Why not?
(I have also called .focus() on the div with the class vex-dialog-message).
The screen reader will instead continue to read the text after the button that was pressed when trying to login (that is running an ajax request that tries to login so the document is never reloaded).
This is what role="alert" is for.
It will interrupt a screen reader (as it is the equivalent of aria-live="assertive") with any immediately important information.
As this is a dialog you may instead want to look at role="alertdialog" to replace your role="dialog".
Additionally when you focus the alert don't make the "Login Failed!" text tabindex="0".
Instead use tabindex="-1" so that you can still focus it programatically but it does not end up in the focus order of the page.
The pattern you currently use will be tricky (not impossible) to get right as your "OK" (submit) button is within a form, so this will send screen readers into "forms mode", which behaves differently from normal reading mode.
As such if you are able to create a custom component there is a great writeup from deque on how best to implement an alertdialog
An example of the markup used there is as follows, notice the use of aria-describedby and aria-labelledby to maximise the chances of the alert being read correctly:
<div role="alertdialog" aria-labelledby="alertHeading" aria-describedby="alertText">
<h1 id="alertHeading">Warning! Street Address Mismatch!</h1>
<div id="alertText">
<p>The street address you entered does not match the Postal Code Data.</p>
<p>Is this the correct format: 12345 Main St. Blvd.?</p>
</div>
<button id="yes">Yes, Format Is Correct</button>
<button>No, Edit Street Address</button>
</div>
Related
I have a button which opens a calendar modal, I know calendars are bad for accessibility but my client insists on it. This is the code that opens that calendar modal.
<div class="col-sm-6 hidden-xs text-right calendar-cta">
<a type="button" onclick="openNav()" href="#" class="btn-primary">Calendar view</a>
</div>
I then have a close button on the model, which is where the accessibility error is being produced. The x is showing up as 'Text not included in an ARIA landmark'. What am I doing wrong? What do I need to add in order for this to stop producing the accessibility error. Any help would be hugely appreciated.
<div id="myNav" class="overlay" role="menu">
<a class="closebtn" tabindex="-1" role="menuitem" aria-label="close calendar view">×</a>
<div class="overlay-content"> </div>
</div>
This is more of a warning than an error. It's not required under WCAG, although it is best-practice, and you should try to do it if you can.
It is a best practice to include ALL content on the page in landmarks, so that screen reader users who rely on them to navigate from section to section do not lose track of content.
https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA11
You should ideally be using HTML semantic sectioning elements, like: <main>, <nav>, <aside>, <header>, <footer>, etc. This warning is saying that all rendered content should be in some sort of containing element that has an ARIA role associated with it.
There's a great chart that maps all of the HTML 5 semantic elements to their implied ARIA roles.
I'd also recommend changing your a.closebtn to a button element and removing tabindex="-1". Since you're not navigating to a different location, but rather doing something that causes a change to the UI, I think that a button is a more appropriate choice. The tabindex attribute isn't necessary and only serves to prevent receiving focus by manually tabbing.
The direct and the short answers to the questions:
What is wrong here is that aria-label does not contain the visible text and it is a concrete WCAG failure under 2.5.3 Label in Name. Why and how it fails is explained under Failure due to the accessible name not containing the visible label text
Two quick and dirty solutions:
Write "close calendar view" instead of "x" if possible and remove the aria-label attribute
Hide the "x" sign from ARIA by putting it inside <span aria-hidden="true">x</a>. The one and only thing the screen reader will read will be "link close calendar view".
The "Text not included in an ARIA landmark" may still show up, it is a false positive when it is hidden from ARIA.
However, the above fix will fix only 2.5.3, but there are more important issues here.
I wonder
Why the role="button" was assigned to an anchor element to make a button, when there is the button element readily available and no link to anywhere was intended.
From Bootstrap:
When using button classes on elements that are used to trigger
in-page functionality (like collapsing content), rather than linking
to new pages or sections within the current page, these links should
be given a role="button" to appropriately convey their purpose to
assistive technologies such as screen readers.
What tabindex="-1" does there. It will remove the button from the tab order and the keyboard user will not be able to reach or operate it. It causes a concrete 2.1.1 Keyboard failure.
Why closing the calendar view button has a role="menuitem". Is there a menu there?
Moreover, the usage of the "x" character as the only visible accessible name of a button is a 1.3.3 Sensory Characteristics failure. It is an assumption that all users know "x" denotes "close". Just like not everyone understands an asterisk denotes "required fields", ">" sign means "next", or 3 bars on top of each other is now a menu called "hamburger menu". These should all have some textual explanation. Aria label makes the explanation for the screen reader users but some sighted users may still fail to understand what is meant there.
Alternative code, not including how the btn-close works: (Close button Bootstrap v. 5.0)
(Please note that Bootstrap does not address the 1.3.3 criterion explained above, that is why I included a tooltip in my suggestion.)
<div class="col-sm-6 hidden-xs text-right calendar-cta">
<button type="button" class="btn-primary" onclick="openNav()">Calendar view</button>
</div>
<div id="myNav" class="overlay">
<button type="button" class="btn-close" aria-label="Close calendar view" title="Close"></button>
<div class="overlay-content"> </div>
</div>
Am using vb.net in my asp.net project also bootstrap 4. I have two dropdown list inside a modal, when the user select an item from the first dropdown list, the second list will be sorted.
The problem is, when the user select from the first list the page will postback which casing the modal to disappear. I tried to use <asp:UpdatePanel></asp:UpdatePanel> tag, but it didn't work.
I would appreciate any help or suggestions! Here's my code:
<div class="modal fade" id="AddNew" data-backdrop="static" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add New</h4>
</div>
<div class="modal-body">
<div class="row pb-3">
<div class="col-lg-6">
<asp:DropDownList ID="DropDownList1" class="browser-default custom-select form-control-sm" runat="server" AutoPostBack="true"></asp:DropDownList>
</div>
<div class="col-lg-6">
<asp:DropDownList ID="DropDownList2" class="browser-default custom-select form-control-sm" runat="server"></asp:DropDownList>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="input-group mb-3 input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<asp:TextBox ID="TextBoxName" class="form-control" runat="server" placeholder="User Name..."></asp:TextBox>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<asp:DropDownList ID="DropDownListGroup" class="browser-default custom-select form-control-sm" runat="server"></asp:DropDownList>
</div>
</div>
</div>
<div class="modal-footer">
<asp:Button ID="ButtonAdd" class="btn btn-primary btn-sm" runat="server" Text="Add" />
</div>
</div>
</div>
</div>
There is not much you can do here. A post back will blow out a jQuery.UI dialog, a ajaxtoolkit popup, and of course a bootstrap dialog.
This means and suggests that while you can pop a dialog and let a user choose or click on something? That action can be though as ALSO the means to close that dialog if the user choice is to cause a post-back (saves you having to write code to close the dialog!!!).
If you need something like a cascade combo box?
Then I would NOT use a popup, but simply use a update panel, and display the several objects.
If you really want the cascade combo box or whatever in the popup? Then you have to use JavaScript and ajax calls to the web page - no post backs allowed. You CAN do this, but it quite a bit of extra work. So, I would suggest that you avoid the popup for such kinds of work, since you not only lose the GREAT nice easy to use button code stubs and code behind (the reason why asp.net and web forms are oh so easy to use!!!).
So, you have to get say a little bit creative. You can put all the options say inside of a "div" section, and hide or show that section with code behind.
And then show/hide the area on the form with say this:
myDiv.Style("Display") = "normal"
So hide/show a area on the form. Thus in that panel, we can have two combo boxes (choose state/province and then cascade to the city choice). When the user selects a city, (and auto postback) then we shove that selecting into the text box, and hide the div. All works with just a few lines of code, and no jQuery or bootstrap dialogs.
So the general rule?
Pop a dialog - user can enter data, check box a few things, enter comments and then they hit ok button or whatever. But that button will do a post-back and will close the dialog. So that is the design pattern.
If you break from above? Then you can build dialog forms, but you have to wire up any actions of that dialog as ajax calls. A post back causes the WHOLE browser page to travel up to server. Code behind runs, changes anything on that page and then the whole page travels back down to client and THEN is re-displayed. I should ALSO add that the web server page is now gone, does NOT exist server side and all variables in code etc. are also gone.
The code behind NEVER interacts with the user. it ONLY interacts with the web page in that round trip and ONLY during the short time that the page is up on the server, and code behind runs. So a post back will blow out any JavaScript running on that page, since the whole page travels up to server, code behind runs, and WHOLE page THEN travels back down to client, the browser loads the page AND THEN STARTS RUNNING the JavaScript. Since a post back sends the page to the server and THEN will send the WHOLE page back, then you can't have a post-back run and keep your JavaScript popus etc. running, and worse the browser re-loads the whole page, and the JavaScript code STARTS over running again!!!
So if you really want to do this? Then you have to make ajax calls.
So the rule and design pattern? A post-back is to close the dialog and you only can have or allow one post-back as the "final" action of that dialog. Break this rule, and you then having to hand code up a LOT of JavaScript and ajax calls.
So the answer is you can't do a post back. You can do this with jQuery.ui dialogs and insert a iframe into the dialog. So you put the asp.net web page and buttons inside of a iframe - but then again, that popup in MOST cases will want to interact with the current page and a iframe does not let you do this for all practical use cases anyway.
I have thus found by changing my design patterns and assumptions, then you simply have to cook up slightly different approaches to your UI designs, and thus once done, then you can do this.
So, for example, you might pop one list to select from, and then when the user selects a value (post back) you then say perhaps popup another list for the user to select. So you can pop a list for display, and the user selection of course can be a post-back (and often it will be). Then code behind can trigger another dialog and you get that input.
I need to have a screen in my app which all of it's content will be read using the screen reader automatically.
I tried to add role="dialog" aria-live="assertive" aria-atomic="true" and it didn't make it.
I also tried to use role="alert" aria-live="assertive" aria-atomic="true" it did read it, but using 'alert' as prefix.
How can I make it happened using no prefixes and additional info ?
I believe the document role is the correct one to use in your case. From MDN:
Generally used in complex composite widgets or applications, the document role can inform assistive technologies to switch context to a reading mode: The document role tells assistive technologies with reading or browse modes to use the document mode to read the content contained within this element.
You should include tabindex="0" on the element in which you wish to be immediately read, and use JavaScript to set the focus to the element.
Update
I tested the following code in Mac/Chrome, version 79.0.3945.88, and removed the tabindex attribute as well as all JavaScript. VoiceOver immediately read the contents of the document in its natural order.
<div class="container" role="document">
<p>
Read this first.
</p>
<p>
Read this next.
</p>
</div>
I did a screen capture of the VoiceOver utility as it was reading the above HTML to show it working on page load (forgive the Giphy Capture bit at the beginning of the gif).
If this really is something like a dialog - which means it consists mostly of operable content (UI controls such as buttons) then the non-operable content will not get announced by default. You can solve this if, on the thing with role="dialog", you have aria-labelledby pointing at its heading, and aria-describedby pointing at all the ids (space-separated) of the elements inside that you want read-out when it opens.
The markup might look like this:
<div role="dialog" aria-labelledby="dialogheading" aria-describedby="foo bar">
<h2 id="dialogheading">Let's have a dialog</h2>
<p id="foo">lorem ipsum</p>
<p id="bar">rhubarb rhubarb</p>
<button>yadda</button>
<button>yoda</button>
</div>
If there's really a lot of non-operable content, or if there's no operable content, role dialog is the wrong thing. If the context for this 'screen' is in forms/application mode use role=document instead, and make sure it has a tabindex so you can give it focus, which should switch the screen reader to browse mode.
I am working on accessibility issue.
I have a spinny/loader which appears on screen when data is getting loaded.
<spinny aria-live="polite" role="alert" aria-label="Loading Page">
So when the spinny appears on screen, screen readers give me alert that spinny is loaded.
Now I want that when the spinny goes away from screen i want the screen reader to provide message such as data loaded or something like that.
I have tried aria-relevant, aria-atomic etc but nothing seems to have worked.
First off, your code sample is specifying conflicting information. Using role="alert" gives you an implicit aria-live="assertive" but you are also specifying aria-live="polite". I would recommend removing role="alert". Having aria-live="polite" is sufficient.
However, if you remove the role from <spinny> (which I'm guessing is a custom html tag?), then your aria-label may not be honored because aria-label'ed things often need a role in addition to the label in order for the label to be read by a screen reader. See "Practical Support: aria-label, aria-labelledby and aria-describedby"
But, I think you might be using aria-label incorrectly anyway. Your live region should look something like:
<div aria-live="polite" class="sr-only" id="myspinny"></div>
(See What is sr-only in Bootstrap 3? for the "sr-only" class. It will visually "hide" the <div> so that any text you put inside it will not be visible to the sighted user but will still be available to screen reader users.)
When data is loading, you should inject text (via javascript) into "myspinny" so that it looks like:
<div aria-live="polite" class="sr-only" id="myspinny">Loading Page</div>
Since the <div> is a live region, the text ("Loading Page") will be announced.
When the data is finished loading and you want to remove the spinner, inject new text into "myspinny" so that it looks like:
<div aria-live="polite" class="sr-only" id="myspinny">Data Loaded</div>
and the screen reader will say "Data Loaded".
I'm integrating Google Sign-in with my web app and it automatically renders a button based on an ID I provide the gapi.signin2.render() function. My concern is that all of the documentation uses a <div> as the button's containing element, so a user is forced to click the button using a mouse instead of having the option to tab to the element and activate it that way. Even after the render() call, the container contains only a mix of <div>, <span>, and <svg> elements, so there's nothing a user could tab to.
Google provides documentation on building a custom sign-in button, but even that uses markup that looks like so:
<div id="gSignInWrapper">
<span class="label">Sign in with:</span>
<div id="customBtn" class="customGPlusSignIn">
<span class="icon"></span>
<span class="buttonText">Google</span>
</div>
</div>
If I try to use a <button> or <a> as the container, I cannot use the enter key while focused on the element to invoke the event handler on the button.
How can I make a Google Sign-in button that's accessible for users who don't use a mouse?
After attempting to find help on the official Google product forums, I solved this by following the documentation on building a custom button, but changing the markup from what is shown there and instead using an anchor tag. So in the end, it looked something like:
HTML:
<a href="#" id="google-signin">
... More markup here ...
</a>
Javascript:
auth2.attachClickHandler(
document.getElementById('google-signin'),
{ 'scope': 'profile email' },
onSignIn,
onFailure
);
The element is now focusable and behaves correctly upon pressing the Enter key.