How the W3C Text Alternative Computation Works

  • 0
  •  0

Written by: Bryan Garaventa

The Text Alternative Computation

Over the years, there has been a lot of confusion about the W3C Text Alternative Computation and how this works, especially when influenced by the addition of CSS and ARIA attributes.

As a bit of forewarning, this article is not primarily meant for general web developers, though having an understanding of these concepts will aid their efforts in building accessible software. Instead, this article is meant to aid browser and assistive technology vendors, as well as those who test and evaluate web technologies for accessibility, in order to identify where the breakdown is between the W3C User Agent Implementation Guide, how browsers implement the naming calculation in the accessibility tree, and how assistive technologies then use this information to convey it to their users.

Firstly, the official Text Alternative Computation is documented at
http://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te

This naming calculation primarily deals with two critical properties in the accessibility tree, the Name and Description. These go by different names depending on the platform and OS being referenced, such as on Windows where the primary text is known as the Name and the secondary text is the Description, whereas on Mac OS X the primary text is known as the Title and the secondary text is known as the Help Text. Regardless, these refer to the same information, where the primary text is critical and the secondary text is supplemental. How these are conveyed to assistive technologies differs depending on the operating system, browser, and the type of assistive technology being used.

(Note: Since the SVG naming calculation is still being worked on, this is not included in the following article.)

Issues with the Text Alternative Computation

The problem historically is that no browser vendors or assistive technology vendors have been able to agree what this algorithm means or how this should translate when conveyed to their users. In many ways, entirely negating the value of the Text Alternative Computation by ensuring that no assistive technology in any combination of browsers will convey the same information reliably or equally to their users.

In an attempt to address this, the following report was made for the W3C that includes five litmus tests that demonstrate the Name and Description calculation as applied to interactive widgets such as form fields, links, and all of the focusable interactive ARIA widget roles: https://github.com/accdc/w3c-alternative-text-computation.

The tests being, if the Name and Description as documented match the Name and Description properties within the accessibility tree of the browser, then the browser passes the recursive algorithm test for processing this calculation. If it does not match all five of these tests however, then the browser does not pass. It’s important to note that these tests are not meant to cover all possible scenarios, but rather to stretch the boundaries of convoluted markup structures within native and interactive widget roles to the breaking point, in order to test the most bleeding edge use cases of the recursion algorithm used within the naming calculation.

As a positive example, work is currently being done to address this, as filed recently with Chrome at
https://code.google.com/p/chromium/issues/detail?id=560900.

The value in using the same algorithm across browsers and accessibility APIs, is that these names can then be more reliably used by assistive technologies to convey the desired names and descriptions for interactive widgets across browsers and operating systems, increasing accessibility and usability for web technologies everywhere.

How the Text Alternative Computation Works

In an effort to clear up some of the misconceptions regarding this calculation, and to note some important caveats to be aware of, the following is a breakdown of how this algorithm works when applied to interactive widgets.

Firstly, as noted earlier, there are two properties that assistive technologies primarily use as part of this calculation, the Name and Description. There are others too, such as Value, but for the sake of simplicity, we will focus on Name and Description here.

This is easiest to imagine as a standard form field that includes a title attribute in addition to an explicit label, like so:

<label for="u_name"> Username </label>
<input title="Must not include spaces or any special characters" type="text" id="u_name" />

When both a Name and Description are set on a widget object in the accessibility tree, both are supposed to be conveyed to the assistive technology user. If both the Name and Description contain the same value however, then this only needs to be conveyed once.

So, according to the Text Alternative Computation, the Name property for the above form field is “Username”, and the Description property is “Must not include spaces or any special characters”.

So far so good. Here is where it gets complicated though. The Text Alternative Computation includes a strict hierarchy for settling on a particular Name, which is also true when calculating the Description.

The Name Property

The algorithm or order of preference for calculating the Name property is as follows:

  1. ‘aria-labelledby’
  2. ‘aria-label’
  3. label (for a form control)
  4. ‘placeholder’ (for a text form control)
  5. figcaption (for a figure)
  6. ‘alt’ (for an img or area)
  7. caption (for a table)
  8. legend (for a fieldset)
  9. text contents (if allowed for this role)
  10. ‘title’
  11. ‘value’ (for an input form control with type of ‘button’, ‘submit’, or ‘reset’)

It’s very important to keep in mind that this algorithm is recursive, and is applied on every element that is included within a widget in order to calculate the total combined Name for that widget.

For example, this recursive naming algorithm is demonstrated using the following HTML markup:

 <button>
  <span class="action"> Delete </span>
  <span class="profile">
    <img src="pict.jpg" alt="Profile" />
    Bryan Garaventa
  </span>
</button>

According to the Text Alternative Computation, the accessible Name for this HTML button element is: “Delete Profile Bryan Garaventa”

Now, this calculation can be changed by modifying the markup, like so:

<button>
  <span class="action"> Delete </span>
  <span class="profile" aria-label="all records of Bryan Garaventa" >
    <img src="pict.jpg" alt="Profile" />
    Bryan Garaventa
  </span>
</button>

Resulting in the Name “Delete all records of Bryan Garaventa”, because DOM traversal stops when aria-label is encountered on the span element.

Similarly, the following updated markup changes the same calculation:

<button aria-label="Remove all trace of Bryan Garaventa from the face of the Earth" >
  <span class="action"> Delete </span>
  <span class="profile" aria-label="all records of Bryan Garaventa" >
    <img src="pict.jpg" alt="Profile" />
    Bryan Garaventa
  </span>
</button>

Which results in the Name “Remove all trace of Bryan Garaventa from the face of the Earth”, because DOM traversal stops on the button element when aria-label is encountered.

The role of aria-labelledby in the naming calculation is very important to understand, because aria-labelledby is only processed once and is not recursive.

A simple demonstration of this is as follows:

 <input id="my_name" aria-labelledby="my_name" aria-label="Your name is?" type="text" />

Which results in the Name “Your name is?”.

The reason being, the algorithm first checks for the presence of aria-labelledby, finds it, then follows the referenced ID to the element, and then applies the same naming algorithm to the referenced element while ignoring all additional instances of aria-labelledby. This is necessary to prevent infinite loops from occurring within browsers that may crash both browsers and assistive technologies.

This logic also allows the algorithm to handle more complex naming calculations, such as the following:

 <div id="parentId" >
  <button aria-labelledby="parentId" aria-label="Remove event:" > X </button>
  <span class="event"> Blindfolded Dart Throwing Contest </span>
</div>

Which results in the button Name “Remove event: Blindfolded Dart Throwing Contest”, because the algorithm detects the presence of aria-labelledby on the button, follows that to the referenced element, then applies the same algorithm to that element and all of its child elements while ignoring any further instances of aria-labelledby.

The Description Property

The same naming algorithm and lack of recursion is used when including aria-describedby to set the Description property in the accessibility tree, which matches that of aria-labelledby.

The algorithm for setting the Description property is as follows:

  1. ‘aria-describedby’
  2. ‘title’
  3. ‘placeholder’ (for a text form control)

When aria-describedby is used to set a Description, the same recursive naming algorithm is used as when setting the Name property, where all additional instances of aria-describedby and aria-labelledby are ignored.

Here is a more complex example that demonstrates this:

<div>
  <div id="lblId">
    Your Email
    <input aria-labelledby="lblId" aria-label="Address" aria-describedby="descId" type="text"
name="email" />
  </div>
  <div id="descId" >
    We need this to spam you forever and ever<span aria-label=" (so there)!" >!</span>
    <a aria-labelledby="hiddenName" aria-describedby="descId" href="#" class="helpBtn"
role="button">
      <span id="hiddenName" style="display:none;">
        What do you mean?
      </span>
    </a>
  </div>
</div>

Here we have two active elements, an edit field and a link that has a role of button, both of which include Name and Description properties in the accessibility tree.

So, according to the naming calculation, the edit field Name is “Your Email Address” and the Description is “We need this to spam you forever and ever (so there)!”.

Also, the Name for the link with role=”button” is “What do you mean?” and the Description is “We need this to spam you forever and ever (so there)!”.

As you can see, even though the link is contained within the same Description container element referenced by the edit field, the Name of the link is not included within the Description calculation for the edit field. This is because aria-labelledby is not recursively traversed as part of the naming algorithm; nor is aria-describedby.

How CSS and ARIA Effect the Naming Calculation

Another commonly misunderstood aspect of the Text Alternative Computation, is how the use of CSS such as display:none and visibility:hidden impact the naming calculation, which is also true for certain ARIA attributes such as aria-hidden and roles such as role=”presentation” and role=”none”.

Equally true, is how CSS pseudo elements such as :before and :after impact the naming calculation when the ‘content’ property is used to render CSS textual content.

CSS pseudo elements

Firstly, CSS pseudo elements are sometimes used to visually convey additional information, which is demonstrated in the following markup:

<!DOCTYPE html>
<html lang="en">
  <head>
    <style type="text/css">
      a[href][target="_blank"]:focus:after, a[href][target="_blank"]:hover:after {
        height: auto; width: auto;
        position: absolute;
        z-index: 1;
        margin-top: 20px;
        background-color: white;
        color: blue;
        font-size: 10px;
        content: ' - Opens in new window ';
      }
    </style>
  </head>
  <body>
    <div>
      <a href="http://google.com" target="_blank" > Search </a>    </div>
  </body>
</html>

This technique uses a CSS pseudo element to display a custom tooltip when a link receives focus or is moused over. Since CSS pseudo elements are included within the referenced element as well, this directly effects the naming calculation.

For example, the accessible Name for the link within this markup is originally “Search”. However, when this link receives focus or is moused over, the Name is automatically set to “Search – Opens in new window”, because this content is included in the naming calculation.

CSS display:none and visibility:hidden

Another factor that influences the naming calculation is the use of display:none or visibility:hidden, and where this is used within the markup. Though these two CSS properties seem similar in that they both hide content, there is a significant difference between them that people need to be aware of.

The CSS property display:none is not inheritable, but visibility:hidden is. Meaning that, if display:none is applied on an element, none of its child elements will include display:none. However, if visibility:hidden is applied on an element, all of its child elements will include visibility:hidden.

References:

Display: https://developer.mozilla.org/en-US/docs/Web/CSS/display

Visibility: https://developer.mozilla.org/en-US/docs/Web/CSS/visibility

This difference directly effects the naming calculation, as demonstrated using the following markup:

<input aria-label="Country" aria-describedby="descId" type="text" />
<div id="descId" style="display:none;" >
  <span>
    Choose the country where you currently reside.
  </span>
</div>

The Name for this edit field is “Country”, and the Description is “Choose the country where you currently reside.”

The reason for this, is that the naming calculation allows for aria-labelledby and aria-describedby to reference hidden elements, however this is only true if it is the referenced element or one of its parent elements that is hidden. This is possible here using display:none, because this CSS property is not inheritable, and thus is not applied to the child span element.

The following example shows why this doesn’t work using visibility:hidden:

<input aria-label="Country" aria-describedby="descId" type="text" />
<div id="descId" style="visibility:hidden;" >
  <span>
    Choose the country where you currently reside.
  </span>
</div>

In this case, the Name for this edit field is “Country”, and the Description is “”.

The reason, because the child span includes the CSS property visibility:hidden as inherited from the parent; thus removing the child span from the naming calculation.

This distinction is important, because all explicitly hidden content within child DOM structures should not be included in the parent naming calculation.

The following example shows why this is needed:

<div id="parentId">
  Email address:
  <input aria-labelledby="parentId" type="text" />
  <div class="validationError" style="display:none;" >
    Error: A valid email address is required.
  </div>
</div>

In this case, as long as the error message is hidden, the Name of the edit field is “Email address:”. However, if a validation error occurs and display:none is dynamically set to display:block, then the Name of the edit field then becomes “Email address: Error: A valid email address is required.”, which will automatically be announced by screen readers when focus is set back to the field.

The same is true when the child element includes visibility:hidden, like so:

 <div id="parentId">
  Email address:
  <input aria-labelledby="parentId" type="text" />
  <div class="validationError" style="visibility:hidden;" >
    Error: A valid email address is required.
  </div>
</div>

Here too the Name of the edit field is “Email address:”, which matches the use of display:none.

How aria-hidden Effects the Naming Calculation

Typically the use of aria-hidden=”true” has the same effect on the naming calculation as CSS display:none.

For instance, the following is equivalent to the prior CSS display:none example:

<div id="parentId">
  Email address:
  <input aria-labelledby="parentId" type="text" />
  <div class="validationError" aria-hidden="true" >
    Error: A valid email address is required.
  </div>
</div>

Where the Name of the edit field is “Email address:”, because the content that includes aria-hidden=”true” is excluded from the naming calculation.

However, aria-hidden has another purpose that contradicts this behavior in the naming calculation, which occurs when aria-hidden is explicitly set to “false”. When applied on an explicitly hidden element using CSS for example, this content is included in the naming calculation even though the element remains hidden using CSS.

The following examples demonstrate this difference:

<div id="parentId">
  Email address:
  <input aria-labelledby="parentId" type="text" />
  <div class="validationError" style="display:none;" aria-hidden="false" >
    Error: A valid email address is required.
  </div>
</div>

And:

 <div id="parentId">
  Email address:
  <input aria-labelledby="parentId" type="text" />
  <div class="validationError" style="visibility:hidden;" aria-hidden="false" >
    Error: A valid email address is required.
  </div>
</div>

Which both result in the edit field Name “Email address: Error: A valid email address is required.”, even though CSS is used to hide the error text.

This occurs because aria-hidden=”false” negates the use of display:none or visibility:hidden on the same element. This is also true when combined with the HTML5 ‘hidden’ attribute.

How role=”presentation” and role=”none” Effect the Naming Calculation

This doesn’t come into play very often, but it is important to understand how the use of role=”presentation and role=”none” impact the recursion algorithm for the naming calculation.

(Both of these roles are exactly the same, so discussing one is the same as discussing the other.)

First, understanding how these effect the naming calculation is easier if the purpose of these roles is clear.

In short, both of these roles nullify the applied element from the accessibility tree, but don’t affect any child elements that are not part of the same markup structure.

Here is a simple example of a standard DOM structure:

<button>
  <div>
    <span> Wow! </span>
  </div>
</button>

Within the accessibility tree, the object hierarchy is as follows: Button > Div > Content; resulting in the button Name “Wow!”.

So, in accordance with the naming calculation, the button Name is changed when aria-label is added like so:

<button>
  <div aria-label="This is the best!" >
    <span> Wow! </span>
  </div>
</button>

Which results in the button Name “This is the best!”, because aria-label is used as the Name when encountered here in the naming calculation.

This is changed however when role=”presentation” or role=”none” is added to the equation, since this nullifies the element within the accessibility tree, like so:

<button>
  <div aria-label="This is the best!" role="presentation" >
    <span> Wow! </span>
  </div>
</button>

Now, within the accessibility tree, the object hierarchy is as follows: Button > Content; resulting in the button Name “Wow!”.

This occurs because the presence of role=”presentation” or role=”none” nullifies the element structure that the role is applied to, and an element that has been nullified cannot have a Name in the accessibility tree.

This is why an HTML img element that includes role=”presentation” or role=”none” is undetectable using assistive technologies, because the image has been nullified in the accessibility tree.

Conclusion

Though the Text Alternative Computation is somewhat complicated, it is based on sound logic, and can be understood with a bit of practice.

Also, having a good understanding of how the naming calculation works will help others in the future to identify remaining browser and assistive technology bugs where this algorithm is not being reliably observed. Browser and assistive technology bugs should be filed whenever this is found to be the case.

Doing this is important, otherwise web technologies will never work consistently or reliably across all devices for all possible user types in the future as web technologies evolve.

No Comments

    Leave A Comment