This is my personal blog. The views expressed on these pages are mine alone and not those of my employer.

Wednesday, December 01, 2010

Tricks for ARIA on the iPad/iOS

I've been doing some work using ARIA on the iPad (iOS 4.2) and wanted to share some of the things I've found for others so they don't have to grope in the dark like I've been :)

Introduction

ARIA on the iPad is implemented using VoiceOver, which when turned on will 'read' everything for a user. When in Mobile Safari there is also something called the Web Rotor, which is a cool virtual 'dial' that appears on the screen when you do a two finger touch and turn it. This dial allows users to jump through your pages in different ways, whether by character, words, links, landmarks, etc.

Navigating in VoiceOver is straightforward. Swipe left and right to simulate 'tabbing' through the elements of the page. Swipe up and down to jump through whatever the Web Rotor setting is; for example, if Web Rotor is set to Lines, it will jump by lines. Using three fingers to scroll the page up and down. Do a single touch to select an element; once selected double-tap your finger on the element to simulate a single mouse click. The element that has the current focus will have a black square around it and VoiceOver will announce the appropriate text with audio.

Note that for the work I'm doing I only have to have the ARIA I'm creating work with VoiceOver on the iPad and not with other screen readers, so take the material below with a grain of salt if you have to do cross-screen reader work.

Synthetic Links

I wanted to be able to have users jump through the page between all the links; some of the things I was working with were 'virtual' links that JavaScript actually activated, such as HTML5 <figures>, etc. It turns out you can have these show up as fake links to VoiceOver and Web Rotor by adding role="link" to the element:

<figure role="link">

tabindex="-1"

ARIA provides the ability to use the tabindex on any element now to control how tab ordering and focus works. It is possible to use tabindex="-1" in order to keep an element from being focusable by the user. However, I was unable to get this working in VoiceOver; either I'm misunderstanding tabindex="-1" or it simply doesn't work in VoiceOver.

For example, let's say I have the following markup:

<h1>This is an H1</h1>
<h2 tabindex="-1">This is an H2</h2>

If I give the H1 focus, and then swipe to the right, my understanding is VoiceOver should not then jump to the H2 and give it focus, but VoiceOver incorrectly gives the H2 focus. This seems like a bug to me.

The workaround I've found, which I'm not happy about, is to use aria-hidden:

<h1>This is an H1</h1>
<h2 aria-hidden="true">This is an H2</h2>

This will then hide the element from the tabindex ordering in VoiceOver. This is not a happy long-term situation however, so if someone has a better way to do this (or perhaps an explanation of how I'm using tabindex="-1" wrong) that would be great.

aria-labelledby

Supposedly you can attach aria-labelledby to any element, giving a list of IDs that provide a descriptive label:

<h1 aria-labelledby="part1 part2 part3">Foobar</h1>

Where part1, part2, and part3 are the IDs of three other HTML elements. When the H1 is given focus, VoiceOver should read the text values inside elements part1, part2, and part3 (if my understanding of aria-labelledby is correct). Unfortunately VoiceOver only seems to do this for BUTTON and INPUT elements that use aria-labelledby, not other elements.

To fake this, combined with the previous tabindex="-1" hack, I have to do the following, using role="heading":

<div role="heading">

<h1 id="testH1" aria-hidden="true">
This is an H1
</h1>

<h2 id="testH2" aria-hidden="true">
This is an H2
</h2>

<div id="testDIV1" aria-hidden="true">
This is a div
</div>
</div>

If ARIA worked correctly in Mobile Safari/iOS 4.2, then the correct markup to achieve this would be:

<div aria-labelledby="testH1 testH2 testDIV1">
<h1 id="testH1" tabindex="-1">
This is an H1
</h1>

<h2 id="testH2" tabindex="-1">
This is an H2
</h2>

<div id="testDIV1" tabindex="-1">
This is a div
</div>
</div>

Update: I was also able to get the above chunk working using role="group", but not when the parent element was a DIV; it only worked on LIs for me:

<ul>
<li role="group">

<h1 id="testH1" aria-hidden="true">
This is an H1
</h1>

<h2 id="testH2" aria-hidden="true">
This is an H2
</h2>

<div id="testDIV1" aria-hidden="true">
This is a div
</div>
</li>
</ul>

Sigh; we really shouldn't have to do this to our markup or our ARIA. Fix these bugs Apple :)

Update: Voice Pause

Technically, you should be able to use a comma to cause VoiceOver to 'pause' between items:

Hello, World!

Will cause VoiceOver to pause at the comma.

What happens if you are using the aria-labelledby trick/hack above in order to have VoiceOver 'talk' out loud multiple elements at once?

<h1>Foobar</h1>
<h2>Zoobar</h2>

VoiceOver will munge Foobar and Zoobar together, speaking them outloud too quickly.

In a sane world you could solve this by adding a comma with CSS, or by using CSS Aural Stylesheets which aren't supported by basically anyone:

<style media="speech">
h1:after, h2:after {
content: ",";
}
</style>

However, Speech Media Query stylesheets aren't supported on iOS 4.2; even if I apply the content style rule with the comma as a general style sheet, VoiceOver doesn't pick up the pause.

It turns out I have to put the comma in my markup itself to have the pause take effect:

<h1>Foobar,</h1>
<h2>Zoobar</h2>

Now VoiceOver will pause between the two elements.

While you're here, why not check out Inkling, the company I work for? We are creating interactive digital textbooks for the iPad. Oh, and we're hiring too :)

Labels: , , ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]