Dev Days, Vilnius May 2019
Léonie Watson, TetraLogical
![]() |
![]() |
![]() |
![]() |
| Chrome | Edge | Firefox | Safari |
| (Google) | (Microsoft) | (Mozilla) | (Apple) |
![]() |
![]() |
![]() |
![]() |
![]() |
| Jaws | NVDA | Voiceover | Narrator | Orca |
| (Freedom Scientific) | (NV Access) | (Apple) | (Microsoft) | (GNOME) |
Screen readers consumed the HTML directly from the browser, and created a virtual model of the content


Screen readers began using platform accessibility APIs to query information from the browser
![]() |
![]() |
![]() |
|
UI Automation (UIA) MS Active Accessibility (MSAA) IAccessible2 (IA2) |
OSX Accessibility Protocol (AXAPI) |
Accessibility Toolkit (ATK) AT Service Provider Interface (AT-SPI) |
main element<main>...</main>
nav element<nav>...</nav>
ol element<ol>
<li>Do this</li>
<li>Do that</li>
<li>Do something else</li>
</ol>
h1 element<h1>...</h1>
checked attribute<input type="checkbox" checked>
checked attributebutton element<button>Play</button>
button elementdiv and span elements as building blocks<div class="toolbar">
<span class="button">Bold</span><span class="button">Italic</span>...
</div>
Attributes that polyfill missing role, name, and state information for screen readers
role attribute70+ roles, including:
aria- attributes45+ states and properties, including:
aria-invalid, aria-requiredaria-pressed, aria-expandedaria-controls, aria-ownsspan element<span id="button">Play</span>
role attribute<span id="button" role="button">Play</span>
tabindex attribute<span id="button" role="button" tabindex="0">Play</span>
<span id="button" role="button" tabindex="0">Play</span>
var button = document.getElementById('button');
button.addEventListener('click', doSomething, false);
button.addEventListener('keydown', function(event) {
if (event.keyCode == 13 || event.keyCode ==32) {
doSomething();
}
});
aria-pressed attribute<span id="button" role="button" tabindex="0" aria-pressed="false">Play</span>
var button = document.getElementById('button');
function doSomething(event) {
if (button.getAttribute('aria-pressed') == 'false') {
button.setAttribute('aria-pressed', 'true');
}
else {
button.setAttribute('aria-pressed', 'false');
}
}
span[aria-pressed="true"] {
color: #fff;
padding: 11px 12px 9px 16px;
text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff;
box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9);
}
aria-pressed attributebutton element<button id="button" aria-pressed="false">Play</button>
class tButton extends HTMLElement {
constructor () {
super();
var shadow = this.attachShadow({ mode: 'open' });
// Additional code
shadow.appendChild(button);
}
}
customElements.define('toggle-button', tButton);
button elementconstructor () {
...
var button = document.createElement('button');
var text = this.innerHTML;
button.classList.add('toggle-button');
button.textContent = text;
...
}
constructor () {
...
function toggle(event) {
if (button.getAttribute('aria-pressed') == 'false') {
button.setAttribute('aria-pressed', 'true');
}
else {
button.setAttribute('aria-pressed', 'false');
}
}
button.addEventListener('click', toggle, false);
...
}
toggle-button element<toggle-button>Play</toggle-button>
toggle-button element<toggle-button aria-pressed="false">Play</toggle-button>
An experimental JavaScript accessibility API:
github.com/WICG/aom
Instead of:
button.setAttribute('role', 'button');
button.setAttribute('aria-pressed', 'false');
It would be:
button.role = 'button';
button.ariaPressed = 'false';
Authors will be able to:
ElementDefinitionOptions objectElementInternals objectAuthors will be able to respond to new types of input event, including:
actionIncrement and actionDecrementactionDismissactionScrollpageUp and actionScrollPageDownWill allow authors to create virtual accessibility tree nodes that do not map to the DOM
Will allow authors to:
Of the past, present, and future
Léonie Watson, TetraLogical