blakenetz
blakenetz

Reputation: 131

Voiceover reading pseudo-elements despite aria-hidden attributes within <label />

I'm creating custom radio / checkbox icons by adding a pseudo-element on a label element with the :before css rule. I've added aria-hidden to the label element, but VO on iOS is still reading the pseudo-element.

I understand that some screen readers will ignore an aria-hidden attribute if the element is providing additional context (this is the case for label elements, since they provide additional information about a connected input element). To get around this I've added a aria-label attribute, but again, this is ignore by VO on iOS. This seems to fix the same problem for other screen reader, browser, and device combinations (Narrator and IE / Edge for example).

I've also tried to add a child span or i element to the label and add the :before css rule and aria-hidden attribute to that, but VO on iOS is still reading the pseudo-element.

Does anyone have any advice for having the screen reader read the correct content?

My basic approach is below (note: won't work in a jsfiddle since I'm not loading my font-face).
You can also view the first example here: http://uatwww.surveygizmo.com/s3/4102902/Basic-Radio

<input type="radio" id="radio1" value="1" name="example" />
<label for="radio1" class="custom-icon" aria-hidden="true" aria-label="example 1">Example 1</label>

<input type="radio" id="radio2" value="2" name="example" />
<label for="radio2">
    <span class="custom-icon" aria-hidden="true" aria-label="example 2"></span>
    Example 2
</label>

<input type="radio" id="radio3" value="3" name="example" />
<label for="radio3">
    <i class="custom-icon" aria-hidden="true" aria-label="example 3"></i>
    Example 3
</label>

<style>
input[type=radio] {
    opacity: 0;
    position: absolute;
    border: 0;
    height: 0;
    margin: 0;
    overflow: hidden;
    padding: 0;
}
input[type=radio] + .custom-icon:before,
input[type=radio] + label .custom-icon:before {
    content: "\26aa";
}
input[type=radio]:checked + .custom-icon:before,
input[type=radio]:checked + label .custom-icon:before {
    content: "\26ab";
}
</style>

Upvotes: 2

Views: 5102

Answers (2)

Adam
Adam

Reputation: 18807

Concerning radio1, the W3C says:

If the current node is hidden and is not referenced by aria-labelledby or aria-describedby, nor referenced by a native host language text alternative element or attribute, return the empty string.

So as long as you reference an element even though it has the aria-hidden attribute, it will be spoken out.

If you want to give an alternative text for an element, you have to set the aria-label attribute on the element:

<input type="radio" id="radio1" value="1" name="example" aria-label="example 1" />
<label for="radio1" class="custom-icon" aria-hidden="true">Example 1</label>

Pseudo elements have different beheviours on browsers, and as you can see the alternative text for :before elements will be given even though the associated element is marked with the aria-hidden attribute.

Upvotes: 1

stringy
stringy

Reputation: 1353

I think the problem is that you are giving confusing instructions to both the browser and screenreader. You have an invisible input with CSS content attached to it, which is then associated to a label which is aria-hidden but also has an aria-label. You’re definitely going to get inconsistent interpretations of that markup across different browser/screenreader combinations.

I’ve used Heydon Pickering’s custom control method successfully on a bunch of sites with no problems. It seems like a simpler version of what you’re aiming for. It accessibly hides the input from everyone except screenreader software, puts the CSS content on a span instead of the label or input. He doesn’t use any ARIA, but if more recent versions of VoiceOver announce the CSS content you can just put aria-hidden on the span and let screenreaders treat the label and input as normal.

Upvotes: 1

Related Questions