Accessibility · Semantics and the A11y Tree

Implicit Roles and How Role Conflicts Resolve

How browsers derive a role from element and attributes, what happens when an explicit role token disagrees with the host language, and why some overrides are rejected.

A role is a token from a fixed vocabulary that tells an assistive technology what kind of thing an object is. There are about eighty of them in WAI-ARIA 1.2, grouped into widgets, document structure, and landmarks. None of them is expressed in markup by default; a browser has to derive one for every element it exposes, and the derivation has three layers that can disagree with each other.

The layers are, in order of application: the role implied by the element name, the role implied by an attribute value, and the role requested by the role attribute. The first two are the host language’s mapping, specified element by element in HTML-AAM. The third is author intent. When all three agree the object is straightforward; the interesting cases are the ones where they do not.

Where the implicit role comes from

For most elements the implicit role is a function of the tag alone. nav is navigation, main is main, aside is complementary, ul and ol are lists, li is a list item, table is a table, tr is a row, td is a cell, h1 through h6 are headings whose level is the digit. Those mappings are stable and rarely surprise anyone.

A second group depends on attributes. An a or area with a valid href is a link; without one it is generic and contributes nothing to the tree beyond its text. An input takes its role from type: text and search map to textbox and searchbox, checkbox and radio map to check box and radio button, range maps to slider, number maps to spinbutton, file maps to button in most mappings, hidden removes the element. A select with the multiple attribute is a listbox; without it, a combobox. A th with scope="col" is a column header; with scope="row", a row header; with no scope, the algorithm guesses from position.

A third group depends on context. header and footer are banner and contentinfo only when their nearest ancestor sectioning element is the body. Inside an article or a section they are generic. section is a region only when it has an accessible name; unnamed, it is generic. form is a form landmark only when named. figure with a figcaption is a figure with a name; without one it is still a figure but unnamed. These context rules exist so that a page does not accumulate landmarks from every wrapper the author happened to use, and they are the reason a section with no heading is invisible to landmark navigation.

Two elements have no useful implicit role at all and are widely used as containers: div and span. Their role is generic. Generic objects are exposed but carry no semantics a screen reader would announce, and, as noted in How a Document Becomes an Accessibility Tree, a generic object cannot be given a name by the author. This is the single most consequential fact about implicit roles: the container most authors reach for is the one that cannot carry semantics.

Scanning the role attribute

The role attribute holds a space-separated list of tokens, ordered from most to least preferred. The algorithm takes the first token that is defined in the supported version of ARIA and is not abstract. Everything after it is ignored. This fallback mechanism was designed for the period when new roles such as switch and searchbox were not yet implemented everywhere: role="switch checkbox" produced a switch in browsers that knew the token and a checkbox in those that did not, with no branching in the markup.

Abstract roles (widget, input, range, select, composite, structure, section, sectionhead, landmark, window, command, roletype) are never valid values. They exist to organise the taxonomy, and a list containing only abstract tokens resolves to no role at all. Case is insensitive, and unknown tokens are skipped without an error, which means a typo such as role="buton" silently yields the implicit role of the element instead of the intended one.

role="none" and role="presentation" are synonyms. They request that the element’s own semantics be dropped while its children remain exposed. The request is conditional: it is refused if the element is focusable, if it carries any global ARIA attribute such as aria-label or aria-describedby, or if it carries a state or property that only makes sense with a role. A table with role="presentation" loses its table semantics and its rows become generic; the same table with tabindex="0" keeps its role, because a focusable object with no role is not a coherent thing to hand to an assistive technology.

<h2 role="tab">Billing</h2>
<!-- role=tab is allowed on h2; the heading semantics are gone -->
<div role="tablist" aria-label="Account sections">
  <div role="tab" aria-selected="true" tabindex="0">Billing</div>
</div>
<!-- the tab needs tabindex to be reachable; role alone adds no focus -->

When the explicit role loses

The specification does not treat role as an unconditional override. ARIA in HTML defines, per element, the set of roles an author may use, and a role outside that set is a conformance error. The set encodes a judgement about which substitutions preserve meaning. A button may be given role="switch", role="tab", or role="checkbox", because those are all things a button-like control can plausibly be. It may not be given role="heading", because a heading is not interactive and the element’s keyboard behaviour would contradict it. An input type="text" may not be turned into a button for the same reason in reverse: the element would keep text-editing behaviour while claiming to be a control that activates.

Implementations enforce this unevenly. Some honour a disallowed role and expose it; some substitute the implicit role; some expose the requested role but keep the native behaviour, producing an object whose role and interaction model disagree. The practical consequence for authors is that a disallowed role cannot be relied on to work anywhere, and the conformance error is the signal that the design is fighting the platform rather than using it.

A related conflict is a role that requires children which are absent, or a role that requires a particular parent. listbox expects option children; option expects a listbox or group parent; tab expects a tablist; row expects a rowgroup, grid, table, or treegrid. When the required context is missing, engines differ: some expose the orphan with its requested role and let the assistive technology cope, some expose it as generic, and some repair the tree by inserting an implied container. None of these repairs is visible in the DOM, and all of them change the order in which a screen reader announces the structure. The relationships themselves, including which roles are required and which are merely recommended, are catalogued in Required Owned Elements and Structural Constraints.

Redundant roles and their cost

<button role="button"> is valid, redundant, and harmless. It appears constantly in code written defensively against older engines. It costs a few bytes and one extra step in role resolution, and it has one real benefit: it documents intent where the element is generated by a template that sometimes emits a different tag.

The pattern becomes harmful when it contradicts. <a href="/x" role="button"> produces an object announced as a button that behaves like a link: it navigates, it can be opened in a new tab, and it appears in the browser’s link list. Keyboard behaviour is unchanged, so the object claims a role whose expected interaction, activation with Space as well as Enter, it does not have. <div role="button"> is the inverse: correct role, no keyboard behaviour, and no focus unless tabindex supplies it. In both cases the role is a claim about behaviour, and the behaviour comes from the element and its handlers, not from the token.

The resolution order also matters for state. A role change can invalidate state attributes that were legal for the previous role: aria-checked on an object that is now a link has no defined meaning, and implementations may drop it or expose it as an unsupported property. When the role and the state vocabulary disagree, the state is the part that disappears, and the object is left with a role that promises interaction it cannot report.

References

  1. Accessible Rich Internet Applications (WAI-ARIA) 1.2 — W3C
  2. ARIA in HTML — W3C
  3. HTML Accessibility API Mappings 1.2 — W3C
  4. HTML Standard — Elements — WHATWG