1: Accessibility API
At the top level, the first concept to understand is the platform Accessibility API, which is an integral part of each Operating System. This is MSAA/UIA/IAccessible2 on Windows, AT-SPI/IAccessible2 on Linux, the OS X Accessibility Protocol on Mac and iOS, and so on.
It may not be obvious, but these top level accessibility APIs have a direct relationship with web technology development, and are critical for the accessibility of interactive ARIA Widgets in particular.
For example, at the platform level in the Windows OS, there is a Checkbox control type. This is documented at
the Microsoft Developer Network's Checkbox Class.
When a control such as this, or of any other type, is rendered as part of the Graphical User Interface (GUI), the control and all of its public properties and states, is included in the Accessibility Tree.
The Accessibility Tree is a hierarchical construct of objects that include accessible names and descriptions, plus supporting states and properties, which Assistive Technologies can interface with to enhance accessibility.
Screen readers for example use the accessible Name of a Checkbox object to convey its name, the Role to convey the control type, the State to convey whether it is focused or checked, and so on every time a user interacts with this particular control.
There are software utilities that make it possible to traverse these Accessibility Tree objects, in order to observe the various properties and states as they change.
One such for Windows OS users is Active Accessibility Object Inspector:
https://msdn.microsoft.com/en-us/library/dd318521(VS.85).aspx
Another for OS X and iOS, is Accessibility Inspector:
OS X: https://developer.apple.com/library/mac/documentation/Accessibility/Conceptual/AccessibilityMacOSX/OSXAXTestingApps.html#//apple_ref/doc/uid/TP40001078-CH210-SW1
iOS: https://developer.apple.com/library/ios/technotes/TestingAccessibilityOfiOSApps/TestAccessibilityiniOSSimulatorwithAccessibilityInspector/TestAccessibilityiniOSSimulatorwithAccessibilityInspector.html#//apple_ref/doc/uid/TP40012619-CH4-SW1
Blind developers who use JAWS For Windows can utilize a JAWS script called BX (developed by Doug Lee):
https://www.dlee.org/bx/jfw_bx15a.exe
Which allows access to the Accessibility Tree objects using the MSAA and UIA maps. Instructions for using BX is detailed at
https://www.dlee.org/bx/bxman.htm
1.1: Times of Yore
To put things into perspective, it is helpful to understand the foundations of the Accessibility Tree and ARIA.
Originally, the Accessibility API on native platforms leveraged the window tree hierarchy. The reason being, that Windows applications needed to be able to propagate mouse and keyboard events upward from an object to its ancestors. This allowed desktop applications to have containers that managed their children, such as Listboxes with child Option objects. The Accessibility API used this hierarchy to determine context, such as the number of child objects matching a specific Role.
As desktop applications became more complex, they started to utilize object trees, instead of relying solely on window handles as they had previously. Thus allowing applications to manage child objects more effectively, in order to process mouse and keyboard events within the context of the same structure. This was when the Accessibility API was bound to the platform object tree, which first occurred when Microsoft implemented OLE Accessibility in 1995; later to be renamed MSAA (Microsoft Active Accessibility). This object structure is now what is referred to as the Accessibility Tree.
When it was realized that the hierarchical DOM structure mirrored the Accessibility Tree structure, in order to propagate events upward to ancestors, it became clear that browsers could leverage the same technique using pertinent Roles and States to map specific control types and their children to the Accessibility API on the hosting platform, as long as the semantic hierarchy and parent/child Roles were correctly applied in the DOM. Browsers could then build an Accessibility Tree using these API mappings.
This has been a bumpy ride however, subject to the reverse engineering of each Accessibility API since the release of Dos in 1981, when Accessibility APIs went from looking at the Dos->framebuffer to sniffing GDI calls in Windows 3.1. This was broken with the switch to Windows 95, which was when the OLE Accessibility API was introduced to provide a series of backdoor processes that screen readers could interface with. Shortly after, to take advantage of marketing around the popularity of ActiveX at the time, Microsoft changed "OLE" to "Active", which is how Microsoft Active Accessibility (MSAA) was named. Later this API was extended and renamed as UI Automation (UIA).
The first attempt at mapping base-line platform concepts from the DOM to the more traditional Windows platform, was made by Richard Schwerdtfeger and T.V Raman while brainstorming in 2002, in order to devise a way of determining the nature of a DHTML widget without first requiring the execution of JavaScript. This is when ARIA DOM attributes were conceived. Since the purpose of ARIA was designed as a detection and mapping mechanism for Roles and States, it set the course for what we know as ARIA today. This is why the use of ARIA does not currently include any scripting behaviors when attributes are applied to the DOM; requiring that developers be familiar with these concepts in advance.
(Historical account provided by Richard Schwerdtfeger and T.V Raman)
1.2: Standard Operating System Controls
Going back to our Checkbox example, the following demonstration shows how specific web components are directly tied to the top level Accessibility API in the Operating System. (Verified using Windows7)
- Through the Control Panel, open Internet Options.
- On the General screen that opens, you will see a checkbox named "Delete browsing history on exit"
- Using a utility such as Object Inspector or BX to observe the Accessibility Tree object, examine the object properties.
You can see that the Role is that of "check box" (44), that the Name is that of "Delete browsing history on exit", and that the State when focused and checked is that of "FOCUSED CHECKED FOCUSABLE" (1048596).
1.3: Standard Web Controls
- Paste the following HTML markup into a text file, and save the new file into My Documents using the name "test.html".
<html><body>
<input type="checkbox" checked title="Delete browsing history on exit" />
</body></html>
This is a standard HTML form field Checkbox control, as documented at
https://www.w3.org/TR/html-markup/input.checkbox.html
- Now open the file test.html within Internet Explorer, and check the Accessibility Tree object for the Checkbox in the same manner as before.
You can see that the Role is that of "check box" (44), that the Name is that of "Delete browsing history on exit", and that the State when focused and checked is that of "FOCUSED CHECKED FOCUSABLE" (1048596 in IE11).
The Accessibility Tree object Role, Name, and State values on the web Checkbox match the software Checkbox control in the Operating System.
1.4: Simulated Web Controls using ARIA
Here is where we can see how important ARIA is, and what its impact is on the Accessibility Tree.
ARIA stands for Accessible Rich Internet Applications, and it provides a method for developers to manipulate the Accessibility Tree through the browser using specific attributes within the markup.
A more detailed introduction to ARIA is available at
https://www.w3.org/WAI/intro/aria
This capability is extremely powerful, and allows developers to force a connection between custom web controls and their equivalent software component types on the Operating System.
- Paste the following HTML markup into the file "test.html".
<span tabindex="0" title="Delete browsing history on exit"
class="checked" style="display:block;width:20px;height:20px;background-color:red;"></span>
This is a standard SPAN element, as documented at
https://www.w3.org/TR/html-markup/span.html
- Now open the file test.html within Internet Explorer, and check the Accessibility Tree object for the SPAN in the same manner as before.
You can see that the Role is that of "grouping" (20), that the Name is that of "Delete browsing history on exit", and that the State when focused is that of "FOCUSED FOCUSABLE" (1048580 in IE11).
In essence, this is a standard SPAN element that has been made focusable using tabindex, and includes a Title attribute, but is nothing more in the Accessibility Tree. There is no mapping to a Checkbox control type with relevant State information on the Operating System.
To change the Role of this element and map it to the Operating System Checkbox control, the ARIA attributes role="checkbox" and aria-checked need to be added to the markup.
- Paste the following HTML markup into the file "test.html".
<span tabindex="0" role="checkbox" aria-checked="true" title="Delete browsing history on exit"
class="checked" style="display:block;width:20px;height:20px;background-color:red;"></span>
This is strictly in accordance with the Roles Model spec for role=checkbox, at
https://www.w3.org/TR/wai-aria/roles#checkbox
- Now open the file test.html within Internet Explorer, and check the Accessibility Tree object for the SPAN in the same manner as before.
Now, you can see that the Role is that of "check box" (44), that the Name is that of "Delete browsing history on exit", and that the State when focused and checked is that of "FOCUSED CHECKED FOCUSABLE" (1048596 in IE11).
The simulated Checkbox control has now been mapped to its software equivalent on the Operating System, which makes it possible for Assistive Technologies like screen readers to convey it properly by accessing supporting states and properties such as the "check box" role, the current CHECKED state, and the accessible name.
Specific to UIA on Windows, all valid Role mappings are listed at:
https://msdn.microsoft.com/en-us/library/windows/apps/hh452705.aspx
(Excerpt: "The table in this section shows how Accessible Rich Internet Applications (ARIA) roles map to Microsoft UI Automation control types and control patterns.")
And valid Properties:
https://msdn.microsoft.com/en-us/library/windows/apps/hh452708.aspx
2: ARIA, Important Details
When ARIA is implemented correctly, you can literally change the nature of a custom web component in order to maximize accessibility for Assistive Technology users.
When used incorrectly however, you can instead break the Accessibility API mapping on the Operating System and cause the same custom web components to become virtually unusable by Assistive Technology users.
As a result, extreme care must always be applied when implementing interactive ARIA Widgets.
There are three primary documents that dictate how ARIA works and should be applied by developers.
2.1: User Agent Implementation Guide
The first document is the UAIG (User Agent Implementation Guide), an introduction to which is available at
https://www.w3.org/WAI/PF/aria-implementation/#intro
The UAIG provides specific guidance for browser and Assistive Technology vendors so that ARIA support can be equally supported by both.
For ARIA technologies to work reliably, both browsers and Assistive Technologies must meet in the middle, so that browsers can properly update the Accessibility Tree as specified, and Assistive Technologies can use the Accessibility Tree to provide accurate functionality for end users.
An ARIA Menu is a simple example of why this collaboration is important.
The Windows platform UI equivalent for this is documented at
https://msdn.microsoft.com/en-us/library/system.windows.controls.contextmenu.aspx
When an ARIA Menu is constructed strictly according to spec, It causes specific events to fire in the browser, which Assistive Technologies then use to ensure accessibility by customizing feedback and behavior.
This process is documented in the UAIG, at
https://www.w3.org/TR/wai-aria-implementation/#mapping_events_menus
It's important to note that, in order for an ARIA Menu to be programmed strictly according to spec, it must meet all of the following requirements:
- If styled vertically, the menu container must includerole="menu".
If styled horizontally, the menu container must includerole="menubar".
- All selectable children within the menu must include one of the following Roles:
- Through scripting:
- Programmatic focus must only be set to elements that include a Role of menu, menubar, menuitem, menuitemcheckbox, or menuitemradio.
- When a menu or submenu opens, focus must be set to the new menu.
- A menu should have only one tab stop.
- The arrow keys should traverse logically through each menu item node as expected, or open or close submenus when applicable.
- If a menu item node opens a submenu (another ARIA Menu construct), the focusable element node that includes role="menuitem" must also include aria-haspopup="true".
- If an external keyboard accessible triggering element opens a menu, it too must include the attribute aria-haspopup="true".
- If programmatic focus is set to the element node that includes role="menu" or role="menubar", the attribute aria-activedescendant must be used to point to the currently selected menu item node that includes either role="menuitem", role="menuitemcheckbox", or role="menuitemradio".
Live examples:
- Open either one of the above pages using IE11.
- Right-click anywhere within the static content on the page to open a regular context menu.
Using a utility such as Object Inspector or BX to observe the Accessibility Tree object, you can see that the parent menu Role is that of "popup menu" (11), that the child menu options have a Role of "menu item" (12), and that the State of the highlighted "menu item" Accessibility Tree object includes FOCUSED.
You can get the same result by right-clicking the Desktop to open a popup menu there, and the Accessibility Tree information will be the same.
Now, activate the "Options (Vertical Dropdown)" button to open the ARIA Menu on the test page, and examine the Accessibility Tree.
You will see that the Accessibility Tree objects exactly match, even down to the State including FOCUSED on the highlighted "menu item" Accessibility Tree object.
This is why proper scripting to ensure programmatic focus is so important when building ARIA Widgets, otherwise the correct events will not fire or be recognized by Assistive Technologies.
This is also why you should never improperly mix ARIA Widget markup with active elements that contradict the Operating System equivalent for that control type, nor allow focus to be set to elements that are not part of the intended Widget construct.
Meaning, focus should only be set to elements that include valid Roles for that Widget type.
Otherwise, the element that has focus will not be the same one that includes the correct Accessibility Tree object information for Assistive Technologies to interface with.
For example, the following is an invalid use of ARIA Menu markup:
<ul role="menu">
<li role="menuitem">
<form>
<input type="text" title="Email" />
<input type="submit" />
</form>
</li>
<li role="menuitem">
<a href="somewhereelse.html">
Help...
</a>
</li>
<li role="menuitem">
<button>
Settings...
</button>
</li>
</ul>
This is invalid markup, because there is no software control equivalent to map such a construct to in the Operating System, and the elements that receive focus don't include the relevant Accessibility Tree object information to match a Menu construct.
Conceptually, this would be equivalent to right-clicking a web page, and within the context menu that pops up, finding a form text field and submit button, plus separate links and buttons within each menu item listed there.
Similarly, the following markup is equally invalid:
<div role="textbox">
<label for="name"> First name:</label>
<input type="text" id="name" name="first-name" />
</div>
At the Accessibility Tree level, the above markup is equivalent to the following:
<textarea>
<label for="name"> First name:</label>
<input type="text" id="name" name="first-name" />
</textarea>
It doesn't make sense to Assistive Technologies, and breaks the Accessibility API mappings in the Accessibility Tree.
The manner in which browsers and Assistive Technologies process all supported Roles, is documented in the UAIG Role Mappings section at
https://www.w3.org/TR/wai-aria-implementation/#mapping_role
All developers implementing ARIA should be familiar with this section as well.
2.2: ARIA Roles Model
The second document is the ARIA Roles Model, available at
https://www.w3.org/TR/wai-aria/roles
The ARIA Roles Model defines the markup requirements for synchronizing front-end coding with the back-end behaviors documented in the User Agent Implementation Guide for browsers and Assistive Technologies.
All developers wishing to implement ARIA must read all of this document, and keep on doing so until it finally makes sense.
Otherwise, misinterpretations and incorrect ARIA implementations will continue to be built across the web as the result of misunderstandings.
For example, the following scenarios are the most common pitfalls that developers fall into when attempting to implement ARIA Widgets:
- Developers add ARIA attributes, but fail to add keyboard support and requisite scripting behaviors that match the Widget type.
- Developers add focus movement and scripting behaviors that don't coincide with the elements that include valid ARIA Roles for that Widget type.
- Developers add some ARIA Roles for a specific Widget type, but fail to add others that are required to complete the Widget type.
- Developers add ARIA Roles, but fail to add required state or property attributes for that Widget type.
- Developers don't understand how ARIA usage is supposed to effect functionality within Assistive Technologies.
There is only one way to truly understand something, and that is to immerse yourself within it.
2.3: ARIA Supported States and Properties
The third document is the ARIA Supported States and Properties, available at
https://www.w3.org/TR/wai-aria/states_and_properties
The Supported States and Properties document specifies all available states and properties that are valid upon specific Roles.
For example, the aria-pressed attribute, documented at
https://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
Is only valid upon elements that have a Role of "button", and is not valid anywhere else.
Live demo:
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Toggles,%20Checkboxes,%20Links,%20and%20Buttons/ARIA%20Toggle%20Buttons/demo.htm
Examining the Accessibility Tree objects on Windows will show that each toggle in the above demo is mapped to a Role of "push button" (43), and that the State is "FOCUSED FOCUSABLE" when aria-pressed="false", or "FOCUSED PRESSED FOCUSABLE" when aria-pressed="true".
All developers wishing to implement ARIA must also be familiar with the Supported States and Properties document, in order to understand which states and properties are valid for specific element and Role types.
2.4: ARIA and CSS
Often CSS is used to apply uniform styling across browsers, using markup that doesn't reflect the control type that is represented.
A basic example of this is styling a link to appear visually as a toggle button.
The problem however, is that CSS has no effect on the Accessibility Tree, causing a disparity to occur between what is visually displayed and what is experienced by the Assistive Technology user.
This can be addressed by binding CSS with ARIA Role and State attributes, which speeds processing for dynamic rendering, automatically matches the correct ARIA Roles and States with equivalent control styling, and provides a visual mechanism for verifying the correct usage of ARIA at a glance.
Speed is improved by reducing the number of DOM changes that must occur in order to synchronize the setting of Accessibility Tree Roles and States with matching styles.
For example, as Accessibility Tree Roles and States are set using ARIA attributes, there is no need to ensure that relevant class names are updated in the DOM at the same time, because this is handled automatically as part of the CSS binding.
This technique also ensures that the correct ARIA Role always matches the visually displayed UI control type.
Similarly, this technique can be used to verify that ARIA Roles and supporting attributes are implemented correctly, by providing a visual representation for the control type only when they match; thus allowing developers to see at a glance when controls are not marked up properly.
The following code is a simple demonstration of this.
<html><head>
<style type="text/css">
a[role="button"][aria-pressed="true"][href] {
/* Add styling for a pressed toggle button. */
color: purple; background-color: yellow;
}
a[role="button"][aria-pressed="false"][href] {
/* Add styling for a toggle button that is not pressed. */
color: yellow; background-color: purple;
}
span[role="button"][tabindex] {
/* Add styling for a standard button. */
color: yellow; background-color: black;
}
</style>
</head><body>
<a role="button" aria-pressed="true" href="#">
Spell Check
</a>
<span role="button" tabindex="0">
Login
</span>
</body></html>
Notice how the CSS styling declaration for each A tag includes three attributes, role="button", aria-pressed, and href.
This enforces all three of the following requirements before the styling will be rendered visually.
- The Role of "button" must be present on the A element.
- The State of aria-pressed must be set to either "true" or "false" on the A element.
- The href attribute must be included within the A element to ensure keyboard accessibility.
The same is true for the SPAN CSS declaration, which includes two attributes, role="button" and tabindex. Thus enforcing the following:
- The Role of "button" must be present on the SPAN element.
- The tabindex attribute must be included within the SPAN element to ensure keyboard accessibility.
This makes it possible to dynamically update one attribute, such as changing aria-pressed from "true" to "false", and the accompanying style will automatically be applied to convey this visually.
Also, if required attributes are missing as the result of developer error, the visual styling will not be applied, making it obvious at a glance which controls are not programmed correctly.
2.5: ARIA Drag and Drop
One of the most confusing interactive widget types to implement, is ARIA drag and drop.
The relevant ARIA attributes for which, are referenced at
https://www.w3.org/TR/wai-aria/states_and_properties#attrs_dragdrop
Specifically: aria-grabbed and aria-dropeffect
The first thing to note, is that the attributes aria-grabbed and aria-dropeffect have no functional effect on the draggability of a component. This must be programmed in advance using JavaScript and CSS to handle mouse and keyboard interaction. The drag and drop ARIA attributes simply convey these relevant behaviors to Assistive Technologies.
This means that, drag and drop will not be made accessible just by adding these attributes.
There are many ways that drag and drop can be implemented, including movable components for dynamic UIs, drag and drop into associated target zones, flexible widgets, and so on.
I'll cover the most common implementations here, defined as:
- Unassociated: Where components are draggable with no associated drop zone.
- Associated: Where components require being dragged to one or more drop zones.
- Internal: Where drag and drop is available on the children of a parent control type.
2.5.1: Unassociated
Unassociated drag and drop implementations are the simplest to implement, and are typically seen on dynamic UIs where page components can be dragged around to rearrange the visual layout.
The draggable region can be made keyboard accessible by adding tabindex="0" to the draggable container element, or to the drag handle element. Scripting is then used to control movement when the arrow keys are pressed.
Additionally, aria-grabbed="false" should be added to either the draggable container element, or to the drag handle element, whichever is designed to be focusable. Then, when the item is ready to be dragged, whether this is by setting focus to the draggable container element or to the drag handle element, then aria-grabbed should be programmatically set to "true". When dragging is no longer available, triggered using onBlur for example, then aria-grabbed should be programmatically set back to "false".
If the drag and drop implementation is meant to be progressively enhanced, so it is usable across both desktop and mobile devices, then the touch event model should be taken into account as well.
The following resource describes all relevant touch events as part of a compatibility index:
https://patrickhlauke.github.io/touch/tests/results/
Which is important to understand when screen readers are involved.
This covers the basics, but there is still no textual equivalent for screen reader users, which will result in a dead tab stop when focus is set to an element without a valid Role.
If a drag handle element is used to initiate draggability, then a Button Role (including an explicit label) is useful to convey this information.
Example syntax:
<div role="region" aria-label="Component Name">
<div class="dragHandle" tabindex="0" role="button" aria-label="Move Component Name" aria-grabbed="false"></div>
<div>
Content...
</div>
</div>
If a draggable container element is used instead, then the following syntax can be used:
<div class="draggable" tabindex="0" role="button" aria-labelledby="contentId" aria-grabbed="false">
<div id="contentId">
Movable content...
</div>
</div>
Or
<div class="draggable" tabindex="0" role="button" aria-label="Move Component Name" aria-grabbed="false">
<div>
<img alt="" src="path/file.jpg" />
</div>
</div>
Or
<img class="draggable" tabindex="0" role="button" aria-label="Move Component Name" aria-grabbed="false" src="path/file.jpg" />
Obviously, the draggable container element technique should not include any other active elements. Otherwise it will appear that there are nested active elements in the Accessibility Tree, which won't be conveyed properly by Assistive Technologies.
There are two other reasons why it is not advisable to set focus to a container element that includes aria-grabbed when the container includes other active elements.
- All static content and active elements within the container will be conveyed to screen reader users as being grabbable or grabbed, even if they are not. (Verified using JAWS15 in IE11 and FF)
- There is no way to reliably convey the draggability of a container on touch screen devices when its content includes other active elements that perform different actions. (This is true for both sighted and non-sighted users.)
To ensure the most reliable results, all draggable components that include other active elements, should also include a drag handle to indicate this functionality visually, as well as for sighted and non-sighted keyboard users as shown in the first example.
Whichever technique is used, a visual indication of this functionality must also be displayed when the draggable container element or drag handle element receives focus, in order to convey to sighted keyboard only users that the component is draggable, as well as the keyboard mechanism for doing this.
One way to accomplish this is to display a tooltip onFocus to convey this functionality. This tooltip can then be associated with the triggering element using aria-describedby so that it appears within the Description property of the Accessibility Tree object for Assistive Technologies to utilize.
Example syntax:
<div role="region" aria-label="Component Name">
<div class="dragHandle" tabindex="0" role="button" aria-label="Move Component Name" aria-grabbed="false" aria-describedby="tooltipId"></div>
<div style="display:none;" id="tooltipId" role="tooltip"> Press the arrow keys to move Component Name </div>
<div>
Content...
</div>
</div>
If you paste the above markup into a test page, and examine the Accessibility Tree object for the focusable drag handle element, you will see that its Description property now states "Press the arrow keys to move Component Name". (Verified using IE11 and FF on Win7)
By associating the tooltip text with the Accessibility Tree object, it is then possible for all Assistive Technologies to utilize the Description property as part of an interoperable Accessibility API feature.
(This can be programmatically disabled for touch screen devices where keyboard functionality is not equivalent to desktop usage, by adding gesture event listeners within the UI to determine the correct modality.)
2.5.2: Associated
Associated drag and drop components require that the dragged component be dropped within the region of a drop zone.
Regarding the accessibility of the draggable component, all of the same issues and solutions apply as previously described for Unassociated drag and drop components.
There are some important differences however.
First, the primary issues:
- It is impossible for non-sighted users to accurately gage where drop zones are located on a page, which is true across both desktop and mobile devices.
- It is overly difficult and time consuming for sighted keyboard only users to manually position a draggable component over a specific drop zone when using the arrow keys as previously described.
- The addition of aria-dropeffect on the drop zone element will help identify a droppable region for Assistive Technologies, but it will not actually make drag and drop accessible for screen reader and keyboard only users.
To solve all of these issues, a different approach is required.
When a draggable component has only one associated drop zone, the simplest and most effective technique, is to provide an actionable element that triggers the drag and drop action automatically with one key press.
An offscreen Link or Button can be used for this purpose, which appears visually onFocus, and is positioned offscreen onBlur for sighted keyboard only users. This acts as both an informative tooltip and a triggering element at the same time.
For this technique to work properly, offscreen positioning must be used instead of the CSS properties display:none or visibility:hidden, in order to ensure accessibility for screen readers that support virtual offscreen models such as JAWS and NVDA.
The label for such a Link or Button requires that all of the following information be conveyed:
- The action to be performed, such as "Move"
- The name of the component it applies to, such as "Component Name"
- The drop zone name to which the action applies, such as "Trash"
Example: "Move Component Name to Trash"
When activated from the keyboard, the drag and drop action is automatically performed, and the desired component is processed in the same manner as though it were dragged and dropped using the mouse.
An example of this can be seen at
https://whatsock.com/tsg/Coding%20Arena/Drag%20and%20Drop/demo.htm
Which supports all of the following interactions:
- Keyboard navigation using Tab or Shift+Tab.
- Up and Down arrow navigation using the virtual offscreen model, as seen using JAWS and NVDA.
- Link navigation on touch screen devices such as iOS.
(For example, swipe left or right with one finger to select a Link using VoiceOver, then double tap to initiate drag and drop.)
In the case of a draggable component that includes embedded active elements, where a drag handle element is present, the same offscreen Link or Button technique can be used.
It's important to note that, in both cases, where a draggable container element or a drag handle element is used, neither the container element nor the drag handle element should receive focus. Since keyboard and screen reader accessibility is managed using an offscreen Link or Button, only the relevant Link or Button should receive focus. This will have no negative effect for sighted mouse users, who will never encounter the offscreen Link or Button.
When a draggable component is associated with multiple drop zones, the offscreen Link or Button technique can be used here as well.
The difference being, that the triggering element should open a Menu, where the desired drop zone can then be activated to initiate drag and drop from the keyboard.
2.5.3: Internal
Internal drag and drop components consist of the draggable children of a parent control type.
Such implementations vary, and are dependent on the parent control type that is implemented. For example, this may be used to rearrange Tabs in a Tablist, or to reorder Rows in a Grid, or to move Options from one Listbox to another, and so on.
As a result, keyboard support will vary as well. Tabs may use Control+Left/Right to drag one Tab left or right, a Grid may use Control+Up/Down to move a Row up or down, Listboxes may use Control+Right to move Options from the left Listbox to the right (or the reverse), etc. Assigned key commands depend on the layout of the UI and what the intended functionality is for the Widget type.
In all of these cases, aria-grabbed can be used to identify which focusable child is or is not grabbed, and aria-dropeffect can be used to identify relevant drop zones and their actions.
Another important note to be aware of, the attributes aria-grabbed and aria-dropeffect don't require the accompaniment of mouse draggability at the same time to be utilized effectively.
An example of this can be seen at
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Listboxes/Sortable/demo.htm
Which is a sortable Listbox Widget.
Keyboard users can:
- Press the Up and Down arrow keys to navigate within the Listbox.
- Press the Spacebar to grab the selected Option.
- Press the Up or Down arrow keys to navigate to another location in the Listbox.
- Press the Spacebar again to drop the grabbed Option into the new location.
Sighted mouse users can:
- Click an Option to grab it.
- Click another Option to drop it in that location.
The attributes aria-grabbed and aria-dropeffect are dynamically updated on each Listbox Option to reflect all of these behaviors as they occur.
3: ARIA Categories
Typically, ARIA Role usage fits into one of five broad categories, Landmarks and Regions, Interactive Widgets, Compound Components, Corner Cases, or Live Regions.
3.1: Landmarks and Regions
Landmarks and Regions are amongst the easiest of ARIA Roles to implement, because they are content grouping mechanisms that aid navigation for Assistive Technology users.
As such, they don't require any keyboard support, nor scripting behaviors to function properly.
All Landmark Roles are defined at
https://www.w3.org/TR/wai-aria/roles#landmark_roles
3.1.1: Application Role
There are important details that you need to be aware of regarding the use of role="application":
https://www.w3.org/TR/wai-aria/roles#application
First, the terminology is important to understand.
Many developers refer to interactive web technologies as applications, because they are dynamic, and often styled to resemble software user interfaces.
The logical assumption then, is to add role="application" to every such perceived 'web application'.
This is an incorrect assumption however, and often causes more harm than good when applied without understanding the Application Role in advance.
The purpose of role="application" is not simply to define a particular web technology as an application, but rather, to instruct an Assistive Technology to pass keystrokes through to the control that currently has focus. The region where this is applied dictates where this behavior occurs.
As of ARIA 1.1, role="application" is no longer mapped as a landmark.
This behavior is valuable when simulated controls require the use of specific keystrokes to function properly, and it becomes necessary to override the default keystroke interception that is done by Assistive Technologies as part of standard interaction.
For example, JAWS and NVDA screen reader users can press the Up and Down arrows to navigate by line, Control+Up/Down to navigate by paragraph, 'h' to navigate by heading, and so on within the Virtual Buffer without passing any of these keys to the web page.
In most cases, this type of interaction is exactly what the user expects and wants.
There are occasions though, when specific control types require the use of these intercepted keystrokes in order to function properly for both screen reader and keyboard only users. This is when role="application" is useful.
An example of this can be seen at:
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Date%20Pickers/ARIA%20Date%20Picker%20(Basic)/demo.htm
Which is an ARIA enhanced date picker.
When the date picker opens and focus is set to the current date, the use of role="application" forces JAWS and NVDA into Applications Mode, allowing the arrow keys to be used to navigate the calendar in a grid pattern, which would not occur without the presence of role="application".
Another misconception is that all form fields and interactive ARIA Widgets need to be surrounded by role="application" to make Assistive Technologies pass keystrokes to them.
This is incorrect, and there are two reasons why this should not be done.
- The use of role="application" causes NVDA to render the entire Application region on one line in the Virtual Buffer when in Browse Mode, making it impossible to discover the content without having to enter each Application region manually by pressing Enter on it to see what it contains. This also prevents NVDA users from using quick navigation keystrokes such as 'f' to jump between form fields, because all form fields included within an Application region are undetectable while focus is outside the region.
- All form fields and interactive ARIA Widget types already support these behaviors without needing role="application".
When role="application" is used, there is one Role that reverses Application Mode behavior when applied within the same region; this is role="document":
https://www.w3.org/TR/wai-aria/roles#document
Similar to role="application", role="document" is often misunderstood. For example, it does not represent a literary document or the entire document structure of a web page as its name suggests.
The Document Role simply defines a container within an Application Landmark that should not enforce a particular mode of navigation, thus overriding role="application" and allowing an Assistive Technology user to navigate the content of that Document region in the same manner as a standard web page.
If there is no need to enforce a particular keyboard interaction model for a specific region on a page, then role="application" should not be used.
If role="application" is used on a particular region however, then all relevant interactive controls must be in the tab order and must be properly labelled, because Applications Mode enforces that only active elements are navigable; unless role="document" is used to override this behavior.
Historically this behavior was most noticeable using screen readers like JAWS and NVDA, where the textual content could not be accessed because it wasn't explicitly tied to an active element in the tab order.
In recent versions of JAWS and NVDA however, overrides have been added so that Applications Mode can be forcibly aborted, providing a mechanism for screen reader users to access textual information that may not have been correctly associated with active elements.
Nevertheless, it is still advisable to only use role="application" when you have a specific need to process keystrokes that are generally intercepted by Assistive Technologies.
3.1.2: Region Role
Another Role that is not listed as a Landmark, but is just as important for this purpose, is role="region":
https://www.w3.org/TR/wai-aria/roles#region
As of ARIA 1.1, role="region" is now mapped as a landmark.
The Region Role is helpful for grouping associated content on a page.
Though all Landmark Roles can optionally be explicitly labeled, the use of role="region" requires an explicit label to identify its purpose for Assistive Technologies.
This is achieved by including either aria-label or aria-labelledby on the same container element that includes role="region".
Example syntax:
<div role="region" aria-label="Unique Name One" >
...
</div>
Or
<div role="region" aria-labelledby="uniqueID" >
<h2 id="uniqueID" > Unique Name Two </h2>
...
</div>
(If a Region begins with a heading, aria-labelledby should be used to reference the heading as its Region label whenever possible.)
Since a named Region can represent any type of content, it can be applied anywhere.
A simple example can be seen at
https://whatsock.com/tsg/Coding%20Arena/ARIA%20and%20Non-ARIA%20Accordions/ARIA%20Accordion%20(Internal%20Content)/demo2.htm
Where each accordion section is represented as a named Region using aria-labelledby to reference the triggering element.
Similarly, all of the primary navigation sections on this page are contained within named Regions.
This allows Assistive Technologies to utilize Region navigation commands to jump between each named Region, or to identify the beginning and ending boundaries of a specific Region.
For example, if using JAWS15 in Internet Explorer or Firefox, you can press R or Shift+R to jump forward or backward between each Region, or press Insert+Control+R to show a list of named Regions and Landmarks on the page; thus simplifying navigation from any location.
3.1.2.1: Scrollable Divs
Scrollable Divs are often used to display variable content using minimal screen real estate, so that users can scroll through the offscreen content as desired without moving the viewport.
This is achieved by setting the CSS property overflow:auto on a Div, in addition to a fixed height and width.
By adding tabindex="0" to the Scrollable Div, the region magically becomes accessible to keyboard only users, because the browser automatically handles the scrolling effect when the arrow keys are pressed.
Since the Div has no explicit Role mapping in the Accessibility Tree however, screen readers see this as a dead tab stop that means nothing.
This is easily corrected though, by adding role="region" plus an explicit label to the Scrollable Div.
Example syntax:
<div tabindex="0" class="scrollable" role="region" aria-label="Message Log">
<div>
Scrollable content.
</div>
</div>
Or
<div tabindex="0" class="scrollable" role="region" aria-labelledby="title-id">
<div id="title-id">
Title Text
</div>
<div>
Scrollable content.
</div>
</div>
Now, when screen reader users encounter the Scrollable Div in the tab order, the region label will be announced and make it clear that this is an intentional UI control.
3.2: Interactive Widgets
Interactive Widget Roles, are all those that specifically map to platform UI equivalents in the Accessibility API.
These include all of the following primary Widget Roles:
- button
- checkbox
- combobox
- grid (including children with relevant Roles: rowgroup,row,rowheader,columnheader,gridcell)
- link
- listbox (including children with relevant Roles: option)
- menu (including children with relevant Roles: menuitem,menuitemcheckbox,menuitemradio)
- menubar (including children with relevant Roles: menuitem,menuitemcheckbox,menuitemradio)
- radio (including ancestor with the relevant Role: radiogroup)
- scrollbar
- slider
- spinbutton
- tablist (including children with relevant Roles: tab)
- textbox
- tree (including children with relevant Roles: group,treeitem)
- treegrid (including children with relevant Roles: rowgroup,row,rowheader,columnheader,gridcell)
All of the above Roles that specify "including children with relevant Roles", must manage programmatic focus by either setting focus to its primary container Role, or to one of its selectable child Roles. If focus is set to the primary container Role,aria-activedescendant (also upon the element with the primary container Role) must be dynamically updated to point to the currently selected child Role. For example, if the element including role="listbox" has focus, aria-activedescendant must be used to point to the currently highlighted element that includes role="option". If focus is set to the element that includes role="option" instead, then aria-activedescendant must not be included.
All of the above Roles that specify "including ancestor with the relevant Role", do not support the use of aria-activedescendant. This is true for all of the standalone Widget Roles as well, requiring that programmatic focus be set to each of these standalone Roles individually. The only exception to this is role="combobox", which supports the use of aria-activedescendant to reference the selected Option within an external Listbox control.
Whenever possible, Widget Roles should always reflect the intended functionality of the control type that is displayed.
At the same time, the Roles Model specification should always be reviewed in advance to ensure that the desired Widget Role matches the intended functionality of the control. This is important, because the Role name by itself may be misleading. For example, a Combobox must not reference a Menu, but instead must reference a Listbox, because the two are not functionally equivalent within Assistive Technologies.
3.3: Compound Components
Compound components occur when controls include more than one type of interactive Widget Role as part of their markup structure.
An example scenario for such a control is a keyboard accessible Grid, where the arrow keys can be used to navigate between each Gridcell, then by pressing Enter or Space, focus moves into the Gridcell to set focus to a series of form fields, or a Slider, or any number of other interactive control types.
Compound components are always challenging to implement, yet the following rules should always be observed when doing so.
- Ensure that all active elements are focusable and accessible from the keyboard.
- Ensure that all focusable elements include valid Role mappings that are applicable to the control type.
- Ensure that all focusable elements include explicit labels.
- Ensure that the keyboard interaction design for accessing all focusable elements is logical.
- Ensure that all keyboard interaction commands are fully documented and conveyed to the user; both for sighted keyboard only users and non-sighted screen reader users.
Though this checklist addresses the most common issues associated with building compound components, it is more than likely that browser or Assistive Technology support bugs will be encountered during this process, so it is always important to test each component thoroughly during this process.
The first step for doing this, is to check the Accessibility Tree to verify that each focusable element is correctly mapped in the Accessibility API, and that each includes appropriate Role and State information for Assistive Technologies.
If the correct Widget Roles and States are applied to the elements that receive focus, but the Accessibility Tree is incorrect, then this needs to be reported to the company that owns the browser.
However, if the Accessibility Tree correctly reflects each Role and State for the control type, but the Assistive Technology is not conveying these correctly, then this needs to be reported to the Assistive Technology vender.
3.4: Corner Cases
Corner cases occur when a control doesn't specifically match any Widget Role in the Accessibility API, or when a particular Widget Role is not sufficient by itself to convey all necessary information to Assistive Technologies in the Accessibility Tree.
An example of the first can be seen at
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Date%20Pickers/ARIA%20Date%20Picker%20(with%20Disabled%20Date%20Ranges)/demo.htm
Which is an interactive date picker control that includes disabled date ranges.
An interactive date picker such as this has no specific Widget Role however, and cannot be definitively mapped in the Accessibility API as a particular control type.
As a result, such a control usually becomes a compound component, because it includes embedded controls of differing types, as well as a corner case, because it includes active elements that don't resemble platform UI controls.
In this case, Link and Button Roles are used to represent each simulated active element within the date picker, even though they are not styled as such. This ensures that programmatic focus is always set to an element that maps to an active element Role in the Accessibility Tree, which is necessary to ensure accessibility for screen reader users.
The most important concept to keep in mind when implementing compound components and corner cases such as these, is to always map focusable elements to valid Roles, even if the Role doesn't necessarily match the visual styling.
The reason being, it is far more important to convey the correct Role and State of a control to Assistive Technology users, than it is to convey what it looks like.
The second type of corner case occurs when the correct usage of a Widget Role is not sufficient to convey all necessary information to Assistive Technology users.
An example of this can be seen at
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Data%20Grids/ARIA%20Data%20Grid%20(Dynamic)/demo.htm
Which is an interactive Grid control that moves programmatic focus between each Gridcell when the arrow keys are pressed.
The Grid however, displays two different types of information, a string value and a toggle value. The string value is styled as a label that includes the string, whereas the toggle is styled as a button with a pressed state.
A Gridcell is still a Gridcell however, and there is no way to convey differing pseudo role types such as these within the Accessibility Tree at the same time, since the Role always remains a Gridcell regardless. For example, if the Gridcell includes an element with role="button", and programmatic focus is set to it, it will break the navigability of the Grid for Assistive Technology users.
In this case, offscreen text is used to indicate the role of "button" and the state of "pressed", which is bound to the Gridcell using aria-labelledby. This conveys the pseudo role and state to screen readers without changing the Gridcell Role in the Accessibility Tree or compromising the keyboard interaction model.
As with all corner cases, there is no single solution to fit all situations, and every case is different.
Nevertheless, programmatic focus must always be set to elements that include valid Widget Roles to ensure accessibility for Assistive Technology users.
3.5: Live Regions
Live Regions occur when specific regions of a page are updated dynamically, and it's important to convey this information to Assistive Technology users. Reference:
https://www.w3.org/TR/wai-aria/terms#def_liveregion
Implementing a Live Region is very simple, which can be accomplished using the aria-live attribute, in combination with aria-atomic and aria-relevant as needed.
Additionally, there are specific Roles that are dedicated as Live Regions, such as Alert, Log, and Status, yet there are important differences to be aware of regarding the use of each.
There are also some important caveats to be aware of regarding Live Region usage, that significantly impact accessibility.
3.5.1: Aria-live Property
The aria-live attribute is the simplest technique for creating a Live Region, documented at
https://www.w3.org/TR/wai-aria/states_and_properties#aria-live
The attribute accepts three values, "off", "polite", or "assertive".
When set to "off", nothing is conveyed to Assistive Technology users when content is updated. When set to "polite", the new content is added to the speech queue so that current speech is not interrupted. When set to "assertive", the current speech queue might be cleared before the new content is announced, which could result in an interruption if Assistive Technologies have programmed this behavior.
Example:
<div>
<script type="text/javascript">
var intVal = 0,
intFN = function(){
setInterval(function(){
document.getElementById('tst1').innerHTML = (intVal++);
}, 1000);
};
</script>
<button onclick="intFN()"> Test </button>
</div>
<div aria-live="polite" id="tst1"></div>
Live demo:
https://whatsock.com/training/demos/lr/aria-live-polite.html
There are two other attributes that dictate how aria-live behaves when set to either "polite" or "assertive", which are aria-atomic and aria-relevant.
When aria-atomic is undefined or set to "false", only the newest text should be announced when added to a Live Region container, regardless whether the container contains additional text or not. This is the default behavior when aria-atomic is not included with the aria-live attribute.
When aria-atomic is set to "true" however, all of the text should be announced when new text is added to the Live Region container, every time the container is updated.
Example:
<div>
<script type="text/javascript">
var intVal = 0,
intFN = function(){
setInterval(function(){
document.getElementById('tst1').appendChild(document.createTextNode(' ' + intVal++));
}, 1000);
};
</script>
<button onclick="intFN()"> Test </button>
</div>
<div aria-live="polite" aria-atomic="true" id="tst1"></div>
Live demo:
https://whatsock.com/training/demos/lr/announce%20all/aria-atomic-true.html
The aria-relevant attribute controls the type of information that is announced, and may be set to "additions", "removals", "text", "all", or a space delimited list of individual token values. For instance, the default value for aria-relevant is "additions text" when aria-live is set, specifying that only newly added text should be announced.
When aria-relevant is set to "removals", only removed text nodes should be announced, and not newly added text.
Example:
<div>
<script type="text/javascript">
var intVal = 0,
intFN = function(){
setInterval(function(){
document.getElementById('tst1').innerHTML = (intVal++);
}, 1000);
};
</script>
<button onclick="intFN()"> Test </button>
</div>
<div aria-live="polite" aria-relevant="removals" id="tst1"></div>
Live demo:
https://whatsock.com/training/demos/lr/aria-live-polite-removals.html
3.5.2: Alert Role
The Alert Role is an especially aggressive type of Live Region, which is always important to be aware of. Reference:
https://www.w3.org/TR/wai-aria/roles#alert
Relevant Live Region attributes: aria-live="assertive" and aria-atomic="true"
When set, the Alert Role ties into the alert system on the Operating System, causing system wide alerts to fire and interrupt speech, no matter where focus is set, even while interacting with unrelated applications.
It's important to note that, when aria-live="assertive" is applied to a container without the use of role="alert", it will not tie into the alert system of the Operating System. This only occurs when role="alert" is included.
Example:
<div>
<script type="text/javascript">
var intVal = 0,
intFN = function(){
setInterval(function(){
document.getElementById('tst1').innerHTML = (intVal++);
}, 1000);
};
</script>
<button onclick="intFN()">
Test </button>
</div>
<div role="alert" id="tst1"></div>
Live demo:
https://whatsock.com/training/demos/lr/announce%20all/role-alert.html
To hear this in action, run JAWS on Windows, open the above test page and activate the Test button, then attempt to interact with any other application on your computer while the test page is running.
Notice that speech is constantly interrupted, and it is impossible to interact effectively with any application on the Operating System. (Verified using JAWS15 on Win7)
For this reason, the Alert Role should always be used sparingly, and to convey critical information only.
3.5.3: Log and Status Roles
3.5.4: Live Region Caveats
Besides the previously covered issues with the Alert Role, there are additional caveats to be aware of regarding the use of Live Regions.
- When Live Region text is announced using a screen reader such as JAWS, NVDA, or VoiceOver, the same voice is used to do so. This means that, when a user is navigating using the arrow keys or by any other navigational keystroke to read the page, there is no way for the user to differentiate the content that is being automatically announced from the text that is announced as part of standard navigation, which is often extremely confusing to the user.
This is why Live Regions should never be applied to auto-rotating carousels, nor to frequently updated content such as stock tickers without a dedicated checkbox for enabling this functionality by the user as a toggleable feature if desired.
This is also why Live Regions should never be over-used within web technologies.
- As you may have noticed in the previous live demos when testing the differences between IE and Firefox using JAWS and NVDA, support for Live Regions varies greatly depending on the combination of Properties and Roles that are used.
For example, as of 08/25/2014 using JAWS, neither the Log nor Status Roles are supported in IE, nor does aria-live="polite" announce only the newest text node when dynamically added to a Live Region in IE, nor does JAWS announce only the removed text node when aria-relevant="removals" in IE. (Verified using JAWS15 in IE11 on Win7)
As a result, the most widely supported technique at present, is to use innerHTML to update the content of a Live Region that includes aria-live="polite" or aria-live="assertive", with the intent of announcing the full content to Assistive Technology users.
Example:
<div>
<script type="text/javascript">
var intVal = 0,
intFN = function(){
setInterval(function(){
document.getElementById('tst1').innerHTML = (intVal++);
}, 1000);
};
</script>
<button onclick="intFN()"> Test </button>
</div>
<div aria-live="polite" id="tst1"></div>
Live demo:
https://whatsock.com/training/demos/lr/aria-live-polite.html
- Whenever text is announced as part of a Live Region, or in any other manner such as through an explicit label or description via aria-label/aria-labelledby/aria-describedby, the announced text will always use the Assistive Technology's current punctuation and pronunciation settings; thus making it impossible to guarantee that specific punctuation symbols or capitalization will be announced.
Thus, neither Live Regions nor ARIA labelling mechanisms should ever be relied upon to convey case sensitive or punctuation dependent information to Assistive Technology users.
5: Keyboard, Touch, and ATs
Since Assistive Technology behaviors and support levels differ, it is always important to consider your target audience.
This is especially true for progressive enhancement, where the same controls may be used across both desktop and mobile devices; subject to both keyboard and touch interaction.
In order to ensure maximum accessibility for the highest percentage of people possible, it is always important to know the idiosyncrasies of the most widely used Assistive Technologies.
A good resource for checking the latest screen reader usage statistics, is the WebAIM: Screen Reader User Survey, available at
https://webaim.org/search/?q=Screen%20Reader%20User%20Survey
Currently:
- JAWS and NVDA on Windows desktops and laptops are the most widely used screen readers for people with blindness.
- VoiceOver on iOS is the most widely used mobile touch screen device screen reader for people with blindness.
- Dragon NaturallySpeaking is the most widely used voice navigation software for people with motor impairments.
- ZoomText is the most widely used screen magnification software for people with low vision.
5.1: Keyboard Accessibility
The first thing that must always be covered is keyboard accessibility.
All interactive controls within web technologies must be actionable using the keyboard. There should be no functionality that can only be achieved using the mouse.
This should always be addressed before even considering the use of ARIA.
It's important to note that this doesn't refer to the practice of adding all active elements to the tab order.
Keyboard functionality should always match the closest platform equivalent for that control type, since this is how simulated controls are mapped in the Accessibility Tree when ARIA is applied.
This is why a standard right-click context menu has only one tab stop, uses the arrow keys to navigate within the control, and then closes when the Tab or Escape key is pressed.
Here is an example of a complex control type, a Data Grid:
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Data%20Grids/ARIA%20Data%20Grid%20(Dynamic)/demo.htm
Within which are many active elements, since each data cell is actionable.
Nevertheless, the grid itself has only one tab stop.
When building complex interactive components, the technique of setting focus to the control with one tab stop, allows keyboard only users to easily navigate past the control by pressing Tab again if they don't wish to interact with the control using the arrow keys; thus aiding navigation.
More examples of this design pattern include all of the following:
Additionally, these keyboard requirements are more fully described at
https://www.ssbbartgroup.com/blog/2013/10/22/why-there-are-only-two-ways-to-make-aria-widgets-programmatically-focusable-for-screen-reader-users/
Which all developers should be familiar with in order to apply the correct focus movement plus ARIA attributes with the correct design pattern.
It is also important to note the differences between native active elements and simulated active elements, and how events are triggered differently for each.
Native active elements are automatically recognized by the browser as being actionable, and keyboard accessibility for these controls is handled automatically as well.
A native active element is any standard form field:
https://www.w3.org/TR/html401/interact/forms.html#h-17.2.1
Or any standard link:
https://www.w3.org/TR/html401/struct/links.html#edef-A
(A standard link will only become keyboard accessible as a link when it includes an "href" attribute. An A tag that includes tabindex="0" instead, will be treated as a simulated active element, and not a native active element.)
A simulated active element, is any other static element type that is used in place of a standard link or form field, such as a DIV or SPAN.
A native active element such as a standard link or button is already keyboard accessible, and will automatically process the onClick handler when attached, even when Enter is pressed using the keyboard.
However, a simulated active element such as a DIV or SPAN will not trigger an onClick handler when Enter is pressed using the keyboard. Instead, tabindex must be used to manage focus movement, and a redundant onKeyDown or onKeyUp must be added in addition to the onClick handler, in order to ensure accessibility for both sighted mouse users and keyboard only users.
(Note: onKeyDown will fire repeatedly when a key is held down, but onKeyUp will not.)
This technique is more fully described at
https://www.w3.org/TR/WCAG20-TECHS/SCR29.html#SCR29-description
A simple example of the process involved in making simulated buttons accessible, is available at
https://whatsock.com/training/demos/buttons.html
Where, only the last button listed is fully accessible. Notice also that the last button (4) is the only one that has a correct Role mapping in the Accessibility Tree.
5.2: JAWS For Windows
Since JAWS is one of the most widely used screen readers on the Windows Operating System, it's important to understand how it works.
First, the following article should be read:
https://www.freedomscientific.com/Support/TechnicalSupport/Bulletin/1665
Which provides a good idea how JAWS works within web technologies.
One important thing to note is that, the above referenced article was written for JAWS 11, and the behavior of role=application as described, significantly changed in later versions of JAWS. For instance, support for role=application was removed in JAWS 12 and 13, and was partially reintroduced in 14 and 15. Now, JAWS will enter Forms Mode when focus is set within an Application region, but can be exited back into Virtual Cursor mode as with any other web content by pressing NumPad+ or Escape. In this way, Applications Mode currently acts the same as Forms Mode.
Also, the article mentions Auto Forms Mode. This is often disabled by many power users however as an annoyance, so both modes should be tested for, one with Auto Forms Mode enabled, and another with it disabled.
(This setting can be found within the Verbosity Dialog, by pressing Insert+V while focus is on a web page.)
Important things to know about event triggering using JAWS:
- When JAWS is in Virtual Cursor Mode for navigating web content, JAWS will activate the onClick event when the Enter key is pressed on an element.
- When JAWS is in Applications/Forms Mode however, JAWS will activate the onKeyDown/onKeyUp events instead, and not the onClick.
For native active elements such as standard links and buttons, this usually causes no problems.
For simulated active elements such as when using DIV or SPAN elements however, this often comes into play.
Within the latest version of JAWS, it is possible to change the event passed through when Enter is pressed while the Virtual Cursor is active, to 'Send the Enter key' instead of simulating a mouse click. This too is done within the Verbosity Dialog, under Link Activation. Nevertheless, this setting is never on by default, and should never be relied upon as an alternative.
When JAWS is in Virtual Cursor Mode, it reads all offscreen content, even if other content is overlayed across it.
When this happens, if Enter is pressed, JAWS will activate an onClick using the coordinates of the content JAWS is announcing, but will instead be clicking whatever content is overlayed across it.
When the Down arrow is pressed to navigate through web content using the Virtual Cursor, JAWS will not trigger the onFocus handler if attached to any focusable elements. The only exception to this is when Auto Forms Mode is activated automatically on some form fields.
Regarding support, JAWS often works best in Internet Explorer, and secondarily in Firefox with slightly less support. JAWS does not often work well in Chrome, especially with interactive ARIA Widgets.
5.3: NonVisual Desktop Access
Since NVDA is the second most widely used screen reader on Windows, understanding how it works is also important.
If you understand how JAWS works using the Virtual Cursor Mode and Applications/Forms Mode to navigate web content, then you will easily understand NVDA.
Instead of Virtual Cursor Mode, NVDA uses Browse Mode, and instead of Forms Mode, NVDA uses Applications Mode, but they are equivalent to each other.
For further details regarding the use of NVDA, visit
https://www.marcozehe.de/articles/how-to-use-nvda-and-firefox-to-test-your-web-pages-for-accessibility/
All of the caveats that were listed for JAWS also apply for NVDA:
- When NVDA is in Browse Mode for navigating web content, NVDA will activate the onClick event when the Enter key is pressed on an element.
- When NVDA is in Applications Mode however, NVDA will activate the onKeyDown/onKeyUp events instead, and not the onClick.
When NVDA is in Browse Mode, it reads all offscreen content, even if other content is overlayed across it.
When this happens, if Enter is pressed, NVDA will activate an onClick using the coordinates of the content NVDA is announcing, but will instead be clicking whatever content is overlayed across it.
Here is an important difference between JAWS and NVDA however.
When the Down arrow is pressed to navigate through web content using Browse Mode, NVDA will automatically trigger the onFocus handler if attached to any focusable elements.
This definitely comes into play when building ARIA Widgets that have one tab stop and require the use of the arrow keys to switch focus between them.
Here is an example that shows why this is important to be aware of:
https://whatsock.com/training/demos/radios.html
Which is an ARIA Radio control that incorrectly uses onFocus instead of onClick to trigger selection.
As opposed to:
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Radio%20Buttons/ARIA%20Radio%20Buttons/demo.htm
Which correctly uses onClick to trigger selection.
Regarding support, NVDA often works best in Firefox, and secondarily in Internet Explorer with less support. NVDA does not often work well in Chrome, especially with interactive ARIA Widgets.
5.4: VoiceOver on iOS
Since VoiceOver is the most widely used screen reader on mobile devices such as the iPad and iPhone, understanding how this differs from other screen readers is important as well.
Firstly, unlike JAWS and NVDA, VoiceOver has no offscreen model for browsing.
This means that VoiceOver can only access what is visually rendered in the viewport, and nothing else that is hidden offscreen or behind layered content.
There is one exception regarding offscreen content though, which is the practice of positioning a container element offscreen, assigning it as a live region using ARIA, and then programmatically using it to announce status messages with VoiceOver.
An example of this can be seen at:
https://whatsock.com/tsg/Coding%20Arena/Web%20Chat%20and%20Dynamic%20Message%20Announcement/Web%20Chat%20(Static)/demo.htm
Which is a basic dynamic chat demo.
(As messages are typed, both the 'now typing' status and new message text when it arrives, are announced by VoiceOver using this technique.)
The problem with the most common offscreen styling however, is that the CSS left and top properties are used to position the live region outside of the viewport bounds.
If the live region includes an ARIA Role, or has a tabindex value, it is sometimes possible to get VoiceOver to accidentally focus upon the region, causing the screen to flip into some blank area that it can't return from, rendering the page unusable.
There is only one CSS class that prevents this from occurring, which should always be used when including offscreen content as part of progressive enhancement across devices.
.offscreenText {
position: absolute;
clip: rect(1px1px1px1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
white-space: nowrap;
}
This styling configuration is based on the research done by Thierry Koblentz, which is more fully described at
https://developer.yahoo.com/blogs/tenydnblog/clip-hidden-content-better-accessibility-53456.html, and updated in 2016 to reflect browser support changes for clip-path and white-space properties to increase accessibility.
Regarding event triggering, using VoiceOver by touch to announce content on the page, will automatically trigger the onFocus handler when VoiceOver reads the content, and will automatically trigger the onBlur handler when focus moves away.
This comes into play when implementing progressively enhanced ARIA Menus for example, which may be designed to disappear when focus moves out of the menu, as would be seen on standard desktop machines for keyboard usage.
5.5: Dragon NaturallySpeaking
For people who require voice navigation software due to motor impairments, Dragon is the most widely used.
Historically, nothing that was built using simulated controls was represented as they are mapped in the Accessibility Tree using ARIA.
The following post explains why:
https://blog.paciellogroup.com/2013/11/short-note-aria-dragon-accessibility/
Recent efforts have been made that may help with this in the future however, as documented at
https://www.speechtechmag.com/Articles/News/Speech-Technology-News-Features/Nuance-Dragon-NaturallySpeaking-13-Promises-Greater-Speed-and-Accuracy-98295.aspx
Drilling down to a more technical level, Mahoney says Dragon was able to revamp its ability to interact with Web applications by leveraging the accessibility standard for Web applications, ARIA (Accessible Rich Internet Applications).
"Most well-formed HTML can be speakable," Mahoney says. "We have good compatibility with well-formed standard HTML. Anyone who leverages the ARIA standard gets very good operability with voice commands with Dragon. Basically, it identifies and names the controls on a Web page so that an external application can communicate with [Dragon]."
Regardless of ARIA support, it is still possible to make simulated controls accessible for Dragon users.
This goes back to keyboard accessibility, and why it is so important to ensure that all controls are keyboard accessible and follow the design pattern for the equivalent Operating System control type.
It may seem non-intuitive that ensuring keyboard accessibility is so important for people who can't use a keyboard, but here is the reason why.
Dragon users have the ability to voice specific key commands in order to control keyboard functionality through speech.
Here is a complex control type that demonstrates this functionality:
https://whatsock.com/tsg/Coding%20Arena/ARIA%20Date%20Pickers/ARIA%20Date%20Picker%20(with%20Disabled%20Date%20Ranges)/demo.htm
Which is a keyboard accessible Date Picker control.
Voice navigation commands for Dragon include the following after the date picker is opened:
- 'press up' or 'press down' to navigate by week.
- 'press left' or 'press right' to navigate by day.
- 'press home' or 'press end' to navigate to the beginning or end of a week.
- 'press pageDown' or 'press pageUp' to navigate by month.
- 'press enter' to save a date and close the date picker.
- 'press escape' or 'press tab' to cancel and close the date picker without saving.
Comprehensive keyboard support is critical for all interactive web technologies.
5.6: ZoomText
For people with low or partial vision, ZoomText is the most widely used screen magnification software.
Regarding ARIA support, the following is stated at
https://www.aisquared.com/support/more/zoomtext_100_release_notes
Support for ARIA and HTML5 in Internet Explorer 9 (IE9) When using Internet Explorer 9 (IE9), ZoomText now supports: ARIA landmarks and HTML5 tags for navigating page sections when using Web Finder, ARIA roles and attributes for announcing clearer descriptions of web page controls and other elements, and ARIA live regions to track and announce pop up bars and alerts (enabled by the Alert check boxes in ZoomText's Program Echo and Navigation > Tracking dialog boxes).
This is good, but currently limited.
Since those using screen magnification software have some vision, it shouldn't be an issue for users to recognize specific control types as long as the layout is intuitive, the styling represents a recognizable control type, and the color contrast is sufficient.
Specific to ARIA, it's important to note that the use of aria-label or aria-labelledby to reference offscreen text, will not be conveyed to screen magnification software users.
Reference:
https://www.w3.org/TR/wai-aria/states_and_properties#aria-label
And
https://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby
The naming calculation that browsers use for these attributes, is documented at
https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
Which is always good to be familiar with.
Lastly, when a Label element is used to set an explicit label for a form field by matching their For and ID attributes, as documented at
https://www.w3.org/TR/WCAG20-TECHS/H44.html#H44-description
A user can click on the visible label and activate the form field.
This is often helpful for many different user types.
If the aria-labelledby attribute is used to set an explicit form field label instead however, this functionality will not be available.