Test the leading edge of accessibility with axe Coconut and axe-core 3.0-alpha
The axe-core team at Deque has been working hard on a set of updates to the axe ecosystem of accessibility testing tools. Let’s go over the latest developments so you can decide which tools work best for you!
Introducing axe Coconut
To make it easy for everyone to test the leading edge of axe-core, we’ve introduced a new free Chrome extension, called axe Coconut. It includes our latest code changes for Shadow DOM (more on that below), with experimental rules also enabled.
The axe Coconut extension, created in the same vein as a Chrome Canary or Firefox Nightly, will allow you to try out our latest APIs and rules before they are released to more stable channels, such as our regular axe Chrome Extension.
You can download axe Coconut for free from the Chrome Store and try it out on all sites, including those with Shadow DOM. You can even use it alongside our regular axe Chrome extension: you’ll notice a different devtools tab, logo, and browser action popup window to differentiate the two. We’d love your feedback as you start testing with it, so we can make any necessary changes to our APIs before releasing the code to everyone.
axe-core 3.0 alpha
To give you an opportunity to provide feedback on our new Shadow DOM APIs and test results before they land in 3.0.0 stable, we’ve begun pre-releasing a series of 3x “alphas” under the tag,
To install the latest axe-core pre-release version using npm:
npm install axe-core@next
This will allow you to test for accessibility inside of and across Shadow DOM subtrees, commonly used in Web Components including the Polymer library. It requires custom APIs that walk the entire flattened DOM tree instead of DOM APIs such as document.querySelector. We handle this for you internally, so you can use the familiar axe-core API to test the entire page.
NOTE: in order to test your shadow DOM and components from third parties, please be sure to never use the closed shadow DOM option as this will prevent axe-core and aXe extensions from being able to look inside the shadow DOM.
What the heck is Shadow DOM?
Other examples of Shadow DOM you may have experienced include HTML5 video tags and inputs, which contain multiple controls inside of a single element: embed a
tag, and you get playback buttons for free. If you enable user-agent Shadow DOM in a supporting browser’s developer tools, such as Google Chrome, you can inspect the entire shadow tree of a video tag:
Shadow DOM as a standard provides developers with the same mechanism for encapsulation browsers have internally relied upon for years. But we can now create our own shadow trees for any HTML element, such as plain DIVs, canvas, or custom elements to name a few.
When you attach custom Shadow DOM to an element (called the host), it creates a boundary at the top of which is a ShadowRoot. The encapsulation of shadow roots means that DOM APIs such as document.querySelector have limited capabilities to pierce the boundary (there is the shadow piercing combinator
>>> but it has limitations on specificity). So, we had to write our own.
Luckily there was prior art for this technique in Google’s Accessibility Developer Tools Library and we got a lot of help from the folks over at Google like Rob Dobson and Alice Boxhall. They are also now using axe-core in their Lighthouse tool and will be using the shadow DOM version when this is released.
Shadow DOM and frame selectors in axe-core results
// CSS Selector in a simple document without iframes
// CSS Selector inside an iframe
// Element inside the shadow DOM of a simple document without an iframe.
// Note the extra brackets 
In plain text, from our API documentation for the axe-core results object:
nodes[n].target: Array of either strings or Arrays of strings. If the item in the array is a string, then it is a CSS selector. If there are multiple items in the array each item corresponds to one level of iframe or frame. If there is one iframe or frame, there should be two entries in target. If there are three iframe levels, there should be four entries in target.
If the item in the Array is an Array of strings, then it points to an element in a shadow DOM and each item (except the n-1th) in this array is a selector to a DOM element with a shadow DOM. The last element in the array points to the final shadow DOM node.
Core Shadow DOM APIs and Test Utilities
Developing and testing the internal engine for axe-core 3x means using some of our latest APIs and test utilities. Unless you’re hacking on the core library, this shouldn’t affect you much. But if you want to contribute to it, you should read our updated developer guide to become familiar with the changes.
A virtualNode object contains the actual HTML node, e.g. a DIV element; an array of any child virtual DOM nodes; and a shadowId, indicating which shadow root the node belongs to. If this is undefined, then the node is just a regular DOM node.
<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start">?</span>
We now pass the virtualNode and the regular DOM node into rule matches functions and the check evaluate functions. There are a couple of new APIs for querying the flattened (composed) tree and plucking a node from the tree, which we use internally where the accessibility tree hierarchy is important.
For writing tests for utilities, checks and rules that need to know about virtualNodes, we created a few test utilities to smooth out the experience. There is a utility for checking for Shadow DOM support, which we use to skip tests on platforms that don’t support shadow DOM, like PhantomJS. There are also utilities for mocking a checkContext and setting up fixtures with Shadow DOM.
Thanks to our partner Mitsue-Links, we added Japanese localization to our infrastructure in addition to Dutch. We’ve also written new documentation for rules and debugging CI.
ARIA 1.1 attributes
Here’s a list of the ARIA 1.1 attributes we determined were widely-enough supported by assistive technologies to be included in axe-core at this time:
- aria-modal, with values: [‘true’, ‘false’]
- aria-keyshortcuts: [string]
- aria-placeholder: [string]
- aria-colindex, aria-colspan, aria-rowindex, aria-rowspan
- aria-colcount, aria-rowcount
- aria-posinset, aria-setsize
- aria-haspopup now allows these values: [‘true’, ‘false’, ‘menu’, ‘listbox’, ‘tree’, ‘grid’, ‘dialog’]