curious1
curious1

Reputation: 14737

VoiceOver and Screen Reader: how to code radio buttons

I use VoiceOver/Chrome on a iPad to test whether accessibility works on a page I am developing. I connect a Bluetooth keyboard to the iPad and click on the Tab key to go through elements on the page. The following code used on the page works with VoiceOver:

<div>
    <div class="radio"> 
        <label tabindex="0">
            <input type="radio"  name="abc" value="0" aria-labelledby="q1015678-2430294-button"> <span id="q1015678-2430294-button">Good</span>
        </label>
    </div>
    <div class="radio">
        <label tabindex="0">
            <input type="radio"  name="abc" value="1" aria-labelledby="q1015678-2430295-button"> <span id="q1015678-2430295-button">Bad</span>
        </label>
    </div>
</div>

However, when I use Screen Reader/Chrome on Windows, the above code pronounces "Good Good" before saying "Good, radio button". The same goes for the other "Bad" radio button when tabbing through the two choices.

What is the proper way of coding radio buttons that works with both VoiceOver and Screen Reader work?

Update

This is the HTML used in the test. VoiceOver fails to discover buttons coded in v2 and v3. It can identify and pronounce correctly "This the beginning", buttons in v1, and "This is the end".

I was able to tab through all buttons with the left/swipe gestures. The problem is to use the Tab key on a keyboard to tab through the buttons. The ios on the iPad is 15.4.1.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
<body>

  <form>

    <div tabindex="0" role="text">this is the beginning</div>

    <div>-----------v1------------</div>

    <div>
      <div class="radio">
        <label tabindex="0">
          <input type="radio" name="abc" value="0" aria-labelledby="q1015678-2430294-button"> <span
            id="q1015678-2430294-button">Good</span>
        </label>
      </div>
      <div class="radio">
        <label tabindex="0">
          <input type="radio" name="abc" value="1" aria-labelledby="q1015678-2430295-button"> <span
            id="q1015678-2430295-button">Bad</span>
        </label>
      </div>
    </div>

    <div>----------v2-------------</div>

    <div>
      <div class="radio">
        <label for="q1015678-2430294-button2">
          <input type="radio" name="ddd" value="0" id="q1015678-2430294-button2"> <span>Good 2</span>
        </label>
      </div>
      <div class="radio">
        <label for="q1015678-2430295-button2">
          <input type="radio" name="ddd" value="1" id="q1015678-2430295-button2"> <span>Bad 2</span>
        </label>
      </div>
    </div>

    <div>-----------v3------------</div>

    <div>
      <label for="xyz1">radio button 1</label>
      <input type="radio" id="xyz1" />
    </div>

    <div>
      <label for="xyz2">radio button 2</label>
      <input type="radio" id="xyz2" />
    </div>

    <div>-----------------------</div>

    <div tabindex="0" role="text">this is the end</div>
    
  </form>

</body>
</html>

If I put the following textbox in the form, the VoiceOver is able to pronounce it correctly with the tab key tabbing through the elements on the page.

<textarea name="ta">This is textarea</textarea>

Update 3

In my tests of v1 (the original code), the spacebar is able to activate a radio button on my ipad in both Safari and Chrome.

Upvotes: 4

Views: 2804

Answers (2)

QuentinC
QuentinC

Reputation: 14882

As explained in this answer, activating the "full keyboard access" option make the radio buttons reachable with tab as they normally should. My first answer giving advice on the code stays valid.

The good news is that what we have observed isn't a bug. Apple made it working like this intentionally.

The bad news is that VoiceOver and full keyboard access aren't playing well together.

For example, when full keyboard access is turned on, I can no longer enable/disable quick navigation, and I have big troubles using the rotor. Several other important VoiceOver functions seem to be affected, too. As a blind and VoiceOver user, I really can't keep full keyboard access enabled.

From there, one can deduce that, as a screen reader users, you aren't supposed to use tab to navigate. You should exclusively be using screen reader specific functions. Here, VO gestures.

It also shows that, in fact, your test is incorrectly designed:

  • Since a VO user isn't supposed to use tab to navigate, if everything can normally be reached and operated with VO gestures, you are fine for screen reader users.
  • Checking that all interactive elements are reachable with tab is in fact another test, for keyboard only users, who have problems with touch interfaces but none with vision.

If, on a PC, you can test both at the same time, it looks like it's not on iPhone and iPad, because Apple seem to have decided to completely separate the two kinds of users. It would be interesting to know why, and where the most goes the macintosh.

Upvotes: 1

QuentinC
QuentinC

Reputation: 14882

At a first glance, two things look strange in your code:

  • The label has tabindex=0
  • The id referenced in aria-labelledby is inside the label

For the first point, the tabindex=0 on the label make that label focusable. However, the label isn't supposed to be focusable. Only the radio button should be, and it is by default without specifying tabindex. This is precisely why you first reach the label by itself, and since you first reach the label by itself, you can't do anything with it.

Solution: remove tabindex=0 from the label

For the second point, it might work, but it's still quite an odd construction. The aria-labelledby has to be used when you need to reference a labelling element different than the normal label.

  • If the <label> references the <input> element in its for attribute, you don't need an additional aria-labelledby.
  • When the text is inside the same <label> as the <input>, you don't need aria-labelledby either.
  • If you want only one part of the enclosing <label> be the actual accessible label, you'd better switch to another more conventional construction where the <input> isn't inside the <label>. Then there will be less confusion when using aria-labelledby, if you want still to have a different accessible label than what's inside the <label>.

I hope it's clear enough. My advice: switch to a simpler construction like this:

<div>
<label for="xyz">Label of the radio button</label>
<span>Some additional text not present in accessible label, if you want</span>
<input type="radio" id="xyz" />
<span>Some additional text not present in accessible label, if you want</span>
</div>

Upvotes: 3

Related Questions