Meaning without markup: Accessibility Object Model

Meaningful markup is essential for accessibility, because it brings semantics. In short, it tells the browser what things are. Proposals for the Accessibility Object Model include a new way to convey semantics: without markup, directly in JavaScript. In this post, we will look at the proposals and their current status.

Beyond the scope of HTML

To get a more detailed overview of how the quality of our markup impacts end users, see my previous post How accessibility trees inform assistive tech. To summarise: our markup gets used to construct accessibility trees, which inform assistive technologies (AT) about what things are on our pages. With this information, AT can offer features like ‘browse by heading’ and advanced table navigation.

There are plenty of semantic elements to choose from when we build a component. The set of HTML elements is enormous. Even if you build a new thing for which no semantic element exists, custom components that combine existing HTML elements let you have at least some of your semantics for free.

Sometimes the set of HTML elements really does not cut it. You’ve created an interface concept for which reasonably no semantic tag exists in HTML. This is where WAI-ARIA comes in: it lets you provide accessibility-relevant information to the browser using standardised attributes, including semantics, states and labels.

Some examples of effective ARIA:

  • with aria-expanded="true|false", you can set the state of a button that expands/collapses content
  • with role="alert" you can turn an element into a live region: when content is updated, the update is announced to AT users

There is also ARIA that can turn elements into elements for which there are already existing HTML equivalents, such as buttons and links. This is not recommended, because apart from changing semantics, it also makes affordances like keyboard behavior the developer’s responsibility. For instance, instead of <span role="button">, it is much safer to use <button type="button"> and get the keyboard behaviour for free. In 2019, styling native buttons is no longer a problem. Certainly not one that warrants accessibility bugs like reduced keyboard support.

How AOM will help

So, let’s say you are making something that passes the first rule of ARIA (paraphrased: “don’t use ARIA if it is not needed”). You could use markup for this: just add the appropriate attributes. Once the Accessibility Object Model is implemented, you could also apply ARIA without markup.

The proposed Accessibility Object Model (AOM) will be “a JavaScript API to allow developers to modify (and eventually explore) the accessibility tree for an HTML page”. In other words: it gives developers direct access to the accessibility tree. In a way, that’s a bit like what Service Workers do for the network and Houdini for style: give developers control over something that was previously done only by the browser. All in the name of an extensible web: control over these low-level features enables developers to experiment and create new things without waiting for the standards process. Perhaps, with AOM, people could define Web Components that don’t exist in HTML just yet.

Having access to low-level features like these, gives developers power (yay!). But at the same time, they also give developers more direct responsibility, regarding aspects like security, performance and accessibility. It can be tricky and time-consuming to implement best practices and fulfill all those responsibilities. Admittedly, is it easier, and often sensible, to use browser defaults. Extending the web like this is probably most practical for teams that are able to invest the time.

AOM is currently developed by people from across Mozilla, Google and Apple. They’ve defined four phases for the project. Let’s look at some of the plans and how they help.

No more ‘sprouting’

Interfaces with a lot of advanced controls can quickly become a sea of attributes, which the HTML spec calls “sprouting”. Even a simple disabled button might require role, aria-label and aria-disabled attributes. That’s a lot of attributes, not just to add, but also to maintain (in markup). To solve this, AOM will let developers set accessibility attributes of an element directly in JavaScript.

For example, to set aria-expanded on an element, you could do it on the element directly:

el.ariaExpanded = true;

Previously, we could only set this attribute to expanded by switching the value of the attribute in the markup. With AOM, the set of ARIA attributes will also exist as properties that DOM nodes can have, so called IDL (Interface Definition Language) attributes. The IDL attributes are said to reflect attributes in the markup. In the above example, that means that upon setting el.ariaExpanded to true, an attribute (if it does not yet exist) is added to el and it is set to true.

The mapping of ARIA attributes to IDL attributes is defined in the ARIA 1.2 specification.

Note that in applications that have some form of virtual DOM, like those built with frameworks including React and Vue, best practices prescribe “don’t touch the DOM, let the framework deal with DOM changes”. In those applications, defining semantics in the markup or a template will probably still be the go-to approach.

Relationships without IDs

In HTML, the id attribute uniquely identifies an element. Form labels make use of this: we associate label attributes with their corresponding input by pointing the label’s for to the input’s id. In ARIA, there are a number of properties that need to point to an id (or multiple ids), like aria-controls (this element controls that element) and aria-describedby (this element is described by this element/these elements).

With AOM, one can associate elements with other elements directly, by assigning them like this:

formField.ariaLabelledBy = formLabel;

Non-DOM nodes in the accessibility tree

In some very specific cases, a web application may contain content that does not exist as DOM nodes. For instance: drawing a train station’s map of available elevators on a canvas. Each elevator has a label, and also a informational popup that can be expanded. This information would currently be lost to assistive technologies. With AOM, you could define an accessibility tree containing all the labels and expanded states. These would then be conveyed to AT, so that users can understand them.

Good for testing

The AOM also aims to bring reading accessibility attributes. The intended use case: testing! With AOM, you would be able to access the names, roles and states of your HTML nodes directly in tests.

Tests can already access an element’s ARIA information by reading out attributes today. With AOM, they would read out what something has actually computed to in the browser’s accessibility tree. This functionality has the potential to make tests better.

Accessibility Events

In JavaScript, we have a number of useful events that let us act on user input like clicks, keyboard presses and touch. In some cases, there is a discrepancy between such events and input from users of assistive technologies.

In her talk Web Components and the AOM at JSConf Asia 2019, Léonie Watson explained that actions like “swipe up” and “swipe down” mean different things to users of VoiceOver on iOS:

When you have a screenreader running on a touch screen, the flick up and flick down gestures [developers] would probably use for adjusting the value of a slider, are already used for screenreader specific commands.

Because screenreader users already use “swipe up” and “swipe down” gestures to trigger screenreader specific commands, it would be impossible for them to use the same gestures for something else in the app you’re developing.

This is why AOM aims to bring events that are specific to assistive technologies, so that AT can design consistent ways to let the user perform certain actions. Apple calls these semantic events.

Some examples of accessibility events:

  • increment and decrement for going to the next item in a custom slider (like using up and down arrow keys)
  • dismiss for closing a modal overlay (like pressing ESC key)

There is a large privacy concern associated with AT-specific events: malicious site owners could use them to detect if someone has a visual or mobility impairment.

Current status

The specifications for AOM are still in progress, and so are implementations. Various browsers have implemented (parts of) AOM). The part that seems to have most implementations is attribute reflection. This feature works for most attributes in Chrome and Edge (enable via about:config) and Safari (pick ‘Accessibility Object Model’ from Experimental Features setting in Developer menu). There is currently no AOM support not in Firefox.

If you look at the IDL tests with AOM flags enabled, you can see how much your browser supports of the attribute reflection part of the specification.

Wrapping up

Simple solutions are often the easiest way to get your product to work for as many people as possible. For example, when for a date of birth field, you decide against a fancy, complex datepicker and rely on a clearly labelled input[type=text] instead. Standard, boring HTML goes a long way. Chances are it will work well for users and make your accessibility auditors happy. But if your product has custom controls that just don’t exist in HTML, you will want to convey the right accessibility properties to assistive technologies.

AOM is an interesting new proposal in which accessibility information gets its own APIs beyond existing DOM methods like setAttribute and getAttribute. Those methods set and get out values that compute into properties of accessibility trees, but they don’t deal with those properties directly. It’s a subtle difference. With direct access to accessibility info, we could set accessibility properties without markup, we could create accessibility trees for things that don’t exist in the DOM (like for contents of canvas elements) and testing accessibility may improve.

Many thanks to Léonie Watson, Wilco Fiers and Havi Hoffman for their feedback on earlier drafts of this post and Anne van Kesteren for answering questions.

Comments, likes & shares

No webmentions about this post yet! (Or I've broken my implementation)