WAI-ARIA role=alert on Static Warning Messages & Stopping VoiceOver for iOS from Speaking CSS Generated Content Icons with aria-hidden

Non-Disappearing Alert

Testing a client travel site I see a static weather travel alert warning that is very visually marked as important on the screen but the screen reader user would have no idea of its importance because there are no accessibility API semantics communicated.

screenshot of travel alert example

CSS Generated Icons Cause VoiceOver for iOS Bugs

There is also a CSS Generated Content glyph icon to indicate visually that this is a warning or error message. The text Travel Alert: is red and bold but it has no heading semantics.

I think the string “Travel Alert:” serves as the text alternative for the warning icon itself so you don’t need additional alt text.

VoiceOver for iOS has a bug or a feature, not sure, that causes it to speak “Face Screaming in Fear” when focused on this bootstrap CSS generated warning icon! That’s crazy right! πŸ™‚ I’m serious go test it out.Β http://pauljadam.com/demos/role-alert.html

aria-hidden=true Prevents VoiceOver for iOS from Speaking CSS Generated Content Icons

Bootstrap code for this had aria-hidden=true already correctly applied to the span that generates the icon but I’ve seen this bug out in the wild on many client sites so glad to see the solution works! Try the before and after example.

voiceover displaying css generated icon but not speaking it

See Details Link Purpose Determinable?

Link Purpose is another issue where a screen reader user could not determine the purpose of the See Details link if they just TAB to the link and only hear “See Details, link”. What exactly are the details I’ll be seeing if I click the link? I recommend placing the aria-describedby attribute on the See Details link with a value that points to the ID reference of the full travel alert string text. This way the actual details text of the travel alert will serve as the accessible description in the a11y API.

Simple Heading with WAI-ARIA role=heading and aria-level=1

Travel Alert: to me serves as a heading that sections off a chunk of content below it so it needs semantics. I’m fine with not changing the HTML tag here and we can just apply role=heading and aria-level=1 to the <strong> element and BOOM it’s a heading! At least to the screen reader users and the a11y API πŸ™‚

screenshot of VO OS X on the aria heading
VoiceOver reads “heading level 1, Travel Alert:

But can we put role=alert on static text alerts?

So we fixed the missing heading, we fixed the warning icon issue, fixed the link purpose issue… But I had this idea that this really could use even more semantics to help convey its importance because it does not pop up after page load or flash on the screen so there’s no ARIA Live Region because there’s no text being inserted or removed from the DOM.

The Mozilla Developer Network Using the alert role article is the first google result and gave me the idea based on example 1,Β https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_alert_role.

Example 1: Adding the role in the HTML code

The snippet below shows how the alert role is added directly into the html source code. The moment the element finishes loading the screen reader should be notified of the alert. If the element was already in the original source code when the page loaded, the screen reader will announce the error immediately after announcing the page title.

<h2 role="alert">Your form could not be submitted because of 3 validation errors.</h2>
Now this does not actually work to speak the alert text after the page loads. You MUST use JavaScript to inject a string into a role=alert container that was already present in the DOM on page load for it to actually speak out loud to the screen reader user! So if I removed the string then reinserted it after page load then it would speak, as long as the alert container itself was in the DOM on page load, just not the text. YES IT’S CONFUSING πŸ˜‰

Results of role=alert on Static Text

So in our travel alert example I add the role=alert attribute/value to the entire alert container and there is NO MAGIC, except if you TAB into the See Details link in which case VoiceOver for OS X will now speak the contents of the alert container which sounds great! Even better because VO OS X does NOT read aria-describedby values by default except after a 7 second delay which is very confusing. This experiment solves that problem and also gives it some logical extra semantics.
TalkBack on Android likes alert roles and speaks the semantics like it’s a heading or some other semantic element.
role=alert on static text does nothing for VoiceOver on iOS but aria-describedby speaks with only a 2 second delay here in Mobile Safari so it sounds great without needing the alert role like OS X VO does.

Live Before & After Demo:


before screenshot
VoiceOver OS X says “visited, link, See Details”.


after screenshot
VoiceOver for OS X says “Travel Alert: Winter Storm Juno, Mid-Atlantic and Northeast United States. See Details, link, alert, visited, link See Details”

VoiceOver OS X “Help Text” or “Hints”:

help tag screenshot
If you wait 7 seconds or so VoiceOver for OS X says the aria-describedby value (also title attributes): “You are currently on a link, inside of HTML content. To click this link, press Control-Option-Space. Β The help tag is:  Travel Alert: Winter Storm Juno, Mid-Atlantic and Northeast United States.”

After on Android in Firefox with TalkBack:

firefox android talkback screenshot
TalkBack/Firefox/Android speaks when focused on the After example Travel Alert: heading: “Travel Alert: heading level 1 alert”.
firefox android see details link screenshot
TalkBack speaks when focused on see details link: “See Details – Travel Alert: Winter Storm Juno, Mid-Atlantic and Northeast United States. See Details link”

4 Replies to “WAI-ARIA role=alert on Static Warning Messages & Stopping VoiceOver for iOS from Speaking CSS Generated Content Icons with aria-hidden”

  1. Great stuff Paul. A couple of thoughts:

    For icon fonts, I always aria-hidden the icons to avoid the VO “alien”. Also, this removes the extra swipes on mobile.

    I’ve also noticed the glyphicon-emoticon issue. Bootstrap uses an icon unicode range that overlaps w/emoticons. I haven’t seen this issue with other icon font services like IcoMoon.io – Bootstrap just needs to update their unicode range.

    The heading and “Travel Alert” are solid additions.

    Following up from my original Twitter reply, I don’t see that role=”alert” adds enough value to merit it’s use. We already have “Travel Alert” in the text. OSX VO and TalkBack provide minor indication that we’re dealing with an alert, which is somewhat redundant.

    The one risk I can think of is that, if some day, AT and browsers change the behavior of how these live region roles(alert, status, log) work, I imagine this might negatively impact the experience. These roles should cause the screen reader to speak their contents, but will this interrupt the page load experience in a weird way?

  2. Does the placement of the role=”alert” impact the read out? For some reason our alert messages aren’t being read out. My gut tells me that the role=”alert” and aria-atomic=”true” needs to be around the that surrounds the actual error text but I’m not totally sure if that’s the issue. Thanks so much for your thoughts and time!!

    Example of the code below:

    Either the user ID or password you entered is incorrect. Please try again.

    new fruity .message({id: ‘#tcId0’,style: ‘error’,closeable: false});

  3. Sorry for some reason the code example didn’t show up.

    Either the user ID or password you entered is incorrect. Please try again.

    new fruity.message({id: ‘#tcId0’,style: ‘error’,closeable: false});


Leave a Reply

Your email address will not be published. Required fields are marked *