Accessibility/WebAccessibilityAPI
Introduction
There's number of objectives on the web to improve accessibility and usability support. The web applications want to provide special support for their users, helping them navigate and perceive the content. The browser has number of add-ons serving to improve accessibility support, for example, the app letting to navigate landmarks on the web page. These tasks requires accessibility API similar to what desktop assistive technologies have.
Also web accessibility API allows in-browser automated accessibility testing of web content, i.e. helpful to check HTML and other standards in the browser is accessible to all users.
On the another hand there's growing need of making graphical content accessible. These are charts, graphs and other various visual stuff that are drawn by HTML canvas or SVG. Also there's tendency to use HTML canvas element in place of DOM because of performance matter, here's an example. They used to define all markup right in JavaScript and they need non-DOM accessibility solution to make the content accessible.
Web accessibility API
The API provides bunch of interfaces that are used to express the web content the way the assistive technologies (AT) knows how to deal with. In other words each piece of semantically meaningful content has associated accessible element the AT operates at. The interfaces allow to receive accessible element properties, traverse the hierarchy and interact with content.
Also the API provides a way to extend existing semantics of the markup and add new semantics for inaccessible content like canvas drawings.
Accessible element
This is a basic interface providing an access to the accessible element.
// yzen:
// States should be a simple JavaScript Set
// Attributes, Actions should be a simple JavaSctipt Map
interface AccessibleElement {
readonly attribute DOMString role;
readonly attribute StateSet states;
readonly attribute DOMString name;
readonly attribute DOMString description;
readonly attribute DOMString value;
readonly attribute AttributeSet attributes;
readonly attribute Object patternOf(DOMString type);
sequence<AccessibleElement>? relationsOf(DOMString type);
readonly attribute ActionSet actions;
void activate(DOMString action);
readonly attribute InteractionSet intreactions;
readonly attribute AccesibleElement? parent;
readonly attribute AccesibleElement? firstChild;
readonly attribute AccesibleElement? lastChild;
readonly attribute AccesibleElement? nextSibling;
readonly attribute AccesibleElement? lastSibling;
readony attribute Node? DOMNode;
attribute AccessibleSource? source;
};
Node interface extension
The accessible element can be requested from a DOM node if the DOM node is accessible, i.e. it expresses semantics meaningful to the assistive technology.
partial interface Node {
AccessibleElement? accessibleElement;
};
Base properties
AccessibleElement .role
- Returns accessible role as string. Examples: "button", "menu", "textfield".
AccessibleElement .states
- Returns a StateSet live object for all accessible states on the accessible element.
AccessibleElement .name
- Returns accessible name.
AccessibleElement .description
- Returns accessible description.
AccessibleElement .value
- Returns accessible value if applicable.
States
The accessible states on the accessible element are presented by StateSet object. It is a live object, i.e. if the states on the accessible element are changed then the object reflects that. Implementation is not required to compute any state until requested.
interface StateSet {
readonly setlike<DOMString>;
boolean hasAnyOf(DOMString ... states);
boolean hasAllOf(DOMString ... states);
};
StateSet .hasAnyOf
- Return true if any of the given states is presented on the accessible element.
StateSet .hasAllOf
- Return true if all given states are presented on the accessible element.
Attributes
The accessible element may support any of attributes outlined below. Also the accessible element may expose its own custom attribute sets.
AccessibleElement .attributes
- Returns a AttributeSet live object for all object attributes exposed on the accessible element. Attribute may have value of any type or it may not have value at all. If attributes on the accessible element are changed then the object reflects the actual state of attributes. The implementation is not required to precompute any of the attributes.
AttributeSet
AttributeSet is an interface aimed to work with {name, value} pairs of accessible attributes, i.e. regardless its name it is map like object. It may contain names of with no value, i.e. name is either presented or not. In this case when the map is described by javascript Object then the object property is expected to have true value. For example, { checkable: true }.
interface AttributeSet {
readonly maplike<DOMString, any>;
boolean hasAnyOf(Object map);
boolean hasAllOf(Object map);
};
AttributeSet .hasAnyOf
- Return true if any of the given object property and its non null value matches a { name, value } pair from the map. If a null value property value matches a map name then value is ignored and is not part of the match.
AttributeSet .hasAllOf
- Return true if all properties of the given object and their values match name/value pairs from the map. If matched property value is null then value is ignored and is not part of the match.
Attribute list
A typical accessible element may support the following attributes:
int groupSize;
- Returns a number of items withing the group this accessible element belongs to. If no group then -1.
int indexInGroup;
- Returns an index in the group this accessible element belongs to. If no group then -1.
int level;
- Returns level in hierarchy the accessible element belongs to. -1 if doesn't belong to any hierarchy.
int minvalue;
- Returns minimum possible value.
int maxvalue;
- Returns maximum possible value;
int step;
- Returns step value (iteration between next values).
Patterns
A pattern is a collection of attributes or methods expressing the accessible element semantics other than AccessilbeElement interface provides. The patterns concept is quite similar to attributes but tends to be more powerful. Basically it's alternative implementation of queryInterface.
Object .patternOf(DOMString type)
- Returns an object for the pattern of given type if supported by the accessible element.
Attributes minvalue, maxvalue, step object attributes may be replaced by Value pattern implementing the following interface:
interface AccessibleValue {
readonly attribute long maxvalue;
readonly attribute long minvalue;
readonly attribute long step;
};
Relations
sequence<AccessibleElement> .relationsOf(DOMString type)
- Returns related accessible elements of the given relation type.
Relation types
This is a typical list of relation types that may be exposed by the accessible element. Note, the accessible element may support custom relations as well.
labelfor
- Referred accessible element is an element that is labelled by this accessible element.
labelledby
- Referred accessible element is a label for this accessible element.
descriptionfor
- Referred accessible element is an element that is described by this accessible element.
describedby
- Referred accessible element is a description for this accessible element.
widget
- Return a widget the item belongs to.
parent
- Return a logical parent of the item.
Actions
The accessible element may support actions that can be invoked on it. For example, jump on a link or press on a button, or it can be generic propose actions like scroll or focus. Certain accessible actions may take extra parameters.
AccessibleElement .actions
- Returns a ActionSet object of actions exposed by the accessible element. The returned object is not live, i.e. it is not updated if the accessible element actions change.
AccessibleElement .activate(in DOMString name, in optional any param)
- Invokes the accessible action.
- Parameters:
- name
- action name to invoke
- param
- used to provide extra context for the action
Performs the given accessible action on the accessible element.
Action list
activate
- Exposed on accessible elements that may be activated. Accessible elements may use other names for this action to emphasise the semantics they expose. For example, jump on links, press on buttons, check and uncheck on checkboxes. Accessible element may provide more than one activation actions. For exmaple, tree item can provide select/unselect as its primary action and expand/collapse as secondary.
focus
- Focus the accessible element. May be different from "activate" action, for example, in case of buttons where "activate" means press.
scroll
- Scrolls the accessible element into view, optionally takes coordinates relative the element to scroll the view to
- Parameter:
- delta of ScrollDelta
ScrollDelta param
directory ScrollDelta {
int x;
int y;
int z;
DOMString mode;
};
ScrollDelta structure describes a scroll change within the accessible element, the scroll change can expressed in different units, for example, it can be in pixels or in pages. The accessible element may provide its custom set of supported modes which has to be described in taxonomy.
ScrollDelta .x, .y, .z
- 3d coordinates in the units provided by mode, y and z might be optional depending on the unit.
ScrollDelta .mode
- A unit the coordinates are measured in. Can be on of the following values:
- pixel - x, y and z (optional) specifies how many pixles to scroll (2d or 3d)
- line - x specifies how many lines to scroll
- page - x specifies how many pages to scroll
ActionSet
Accessible actions are presented by ActionSet map like object of pairs { action name, action object }.
interface ActionSet {
readonly maplike<DOMString, Action>;
};
Each action is presented by Action interface.
interface Action {
stingifier readonly attribute name;
readonly attribute description;
readonly InteractionSet interactions(optional DOMString device);
};
Action .name
- Action name
Action .description
- Localized action description
Action .interactions
- Set of interactions (like keyboard shortcuts or mouse gestures) to invoke the action.
- Parameters
- device (optional)
- device name like 'keyboard' or 'touchscreen' the interactions should be returned for.
Here's an example of script announcing available actions on the accessible element.
var set = accElm.actions;
if (set.size() == 0) {
say("no actions on accessible element");
} else {
say("accessible element has " + set.size() + " actions");
set.forEach(action => say(action.description));
}
Interactions
A widget may support actions that can be invoked through variety of devices or by AT like screen readers. The way the action is triggered by the user is described by interaction. Examples of user interactions are swipe, a mouse gesture to toggle a switch control; space, a key to toggle a checkbox. Access keys (alt+letter) and keyboard shortcuts (ctrl+shift+letter) used to activate the widgets are also examples of interactions. Complex widgets like grid control may support several actions, for example ctrl+A for selectall action, ArrowDown/ArrowUp keys for movedown/moveup actions, ArrowRight/ArrowLeft keys for movenext/moveprev actions.
The AT may need to know the used shortcuts to avoid possible conflicts or to supply a native way to invoke the action. For example, if presentation supports swipe gesture to navigate the slides then the AT can announce this info for the user.
AccessibleElement .interactions
- Return an InteractionSet object describing all interactions that can be performed on the accessible element.
interface InteractionSet {
readonly maplike<DOMString, Interaction>;
boolean hasAnyOf(sequence<DOMString> interactions);
readonly attribute InteractionSet allOf(DOMString action, optional DOMString device);
};
AccessibleElement .hasAnyOf
- Return true if all given interaction are in use by the widget.
- Parameters:
- interaction
- list of interactions
AccessibleElement .allOf
- Return a list of all interactions on the accessible element as InteractionSet used to invoke the given action on the given device.
- Parameters:
- action
- action name
- device
- a device name, examples of supported values are "keyboard", "mouse", "touchpad".
interface Interaction {
stingifier readonly attribute description;
readonly attribute device;
};
Here's an example of the screen reader announcing all interactions of swipe action.
var actions = accElm.actions;
actions.forEach(action => { var set = action.interactionsOf();
say(action.name);
set.forEach(item => say(interaction.description));
});
Parent-child relations
The interface provides a bunch of methods to provide access to parent/child relations between accessible elements.
AccessibleElement .parent
- Returns the parent accessible element if any.
AccessibleElement .firstChild
- Returns the first child accessible element if any.
AccessibleElement .lastChild
- Returns the last child accessible element if any.
AccessibleElement .nextSibling
- Returns the next sibling accessible element if any.
AccessibleElement .previousSibling
- Returns the previous sibling accessible element if any.
Semantics providers
The accessible element can be created for different kinds of sources. Most of the accessible elements are created for a DOM element to expose its semantics to the assistive technology. In some cases the browser may not have underlying DOM node for the content. In this case the accessible object having no associated DOM node is created. In some cases the web author may need to change the semantics for existing element or add new semantics or even may need a whole new accessible subtree to make the content accessible. In this case the author needs to describe desired semantics in JavaScript. See JavaScript accessible tree for details.
AccessibleElement .DOMNode
- Returns the DOM node associated with the accessible element if any. The accessible element does not have a DOM node, either when it has been unattached from the DOM node or it's not DOM node based. The first case happens when the JavaScript holds reference to the object longer than the object life cycle. The second case happens because of browser-specific implementation or in JavaScript created trees.
AccessibleElement .source
- Returns AccessibleSource associated with the accessible element if any. Also used to provide an accessible source to override accessible properties of the element. A source change is equivalent to accessible element removal and new accessible element reinsertion.
Content traversal
This section describes how to traverse the accessible content, for example, if you need to navigate a document by headings or navigate a paragraph by words.
A key concept of the content traversal is accessible position which describes a "point" in the document content. A position can be at accessible element, for example, on a button. It can be before or after accessible element, or inside of the accessible element text, for example, at the end of the first line of a paragraph.
The position can be moved forward or backward in the document to the content complying the given criteria. For example, criteria can be described verbally like "move the position forward to next heading" or "move the position one word back".
Accessible Position
[Constructor(AccessibleElement root, AccessibleElement elm, WhereBit where),
Constructor(AccessiblePos pos)]
interface AccessiblePos {
readonly attribute AccessibleElement? anchor;
long compare(AccessiblePos pos);
boolean move(Direction dir, Filter or TextUnit rule);
};
AccessiblePos .anchor
- Returns the accessible element the position is contained by. If the position is at element then element itself.
AccessiblePos .compare
- Returns -1 if the given position is before in accessible tree than this position, 0 if they are the same, 1 if the given position is after.
Construction
AccessiblePos .Constructor
- Constructs the accessible position.
- Parameters
- root of AccessibleElement
- root of subtree the position will belong to
- elm of AccessibleElement
- anchor accessible element of the position
- where of WhereBit
- describes where position should be placed relative anchor element
enum WhereBit {
"before",
"at",
"after",
"inside start",
"inside end"
};
WhereBit .before
- Used to set the accessible position right before the accessible element
WhereBit .at
- Used to set the accessible position at the accessible element
WhereBit .after
- Used to set the accessible position right after the accessible element
WhereBit .inside start
- Used to set the accessible position inside of the accessible element right in the beginning of it
WhereBit .inside end
- Used to set the accessible position inside of the accessible element in the end of it
AccessiblePos .Constructor
- Constructs the accessible position equal to given position.
- Parameters
- pos of AccessiblePos
- accessible position to copy
Move through the content
AccessiblePos .move
- Move the accessible position in the content. Returns true if succeeded.
- Parameters
- dir of Direction
- direction the position will be moved in
- rule of Filter or TextUnit
- describes how position can be moved
enum Direction { "forward",
"backward",
"cyclic forward",
"cyclic backward",
//yzen
"first",
"last",
"toPoint"
};
enum TreeFilterResult {
true, // means 'accept'
false, // means 'skip'
"accept",
"reject",
"skip"
};
callback TreeFilter = TreeFilterResult (AccessibleElement);
enum TextUnit {
"char",
"word",
"sentence",
"line",
"paragraph",
"change",
"bit"
};
TextUnit .char
- Used to move the position one char long.
TextUnit .word
- Used to move the position to the next word.
TextUnit .sentence
- Used to move the position to the next sentence.
TextUnit .line
- Used to move the position to beginning of next line or end of previous line.
TextUnit .paragraph
- Used to move the position to beginning of next/previous paragraph.
TextUnit .change
- Used to move the position to next change either of text attribute or new accessible.
TextUnit .bit
- Used to move the position to next/previous navigable position. For example, from accessible start inside of it to right before accessible outside of it or moving from end of this line to start of next line in case of soft line break lines.
Virtual cursor
Virtual cursor is accessible position attached to the accessible document. It's unique per document.
partial interface AccessibleDocument {
AccesiblePos cursor;
};
Whenever the virtual cursor position is changed then cursor_moved event is fired.
Text
partial interface AccessiblePos {
DOMString textInBetween(AccessiblePos pos);
readonly attribute AttributeSet textAttributes;
};
AccessibleDocument .textInBetween
- Returns the text enclosed between this and given accessible positions.
Example how to get first line of the first encountered paragraph:
var startPos = new AccessiblePos(doc, doc);
startPos.move("forward", a => a.role == "paragraph");
var endPos = new AccessiblePos(startPos);
endPos.move("forward", "line");
var lineText = startPos.textInBetween(endPos);
Text attributes
AccessibleDocument .textAttributes
- Returns a AttributeSet of all text attributes presented at this accessible position.
List of text attributes [to complete]
color
- Text color
background-color
- Background color of text
Caret and selection
Document accessible provides bunch of methods to control selection and caret.
dictionary AccessibleRange {
AccessiblePos start;
AccessiblePos end;
};
partial interface AccessibleDocument {
attribute AccessiblePos caret;
readonly attribute sequence<AccessibleRange> selections;
void select(AccessiblePos start, AccessiblePos end);
void addToSelection(AccessiblePos start, AccessiblePos end);
void removeFromSelection(AccessiblePos start, AccessiblePos end);
};
AccessibleDocument .caret
- Returns accessible position for the caret.
AccessibleDocument .selections
- Returns list of accessible ranges for selected content. A range is pair of start and end accessible positions.
AccessibleDocument .select
- Selects the content between given positions. Clears previous selection.
AccessibleDocument .addToSelection
- Selects the content between given positions. Previously selected content stays selected.
AccessibleDocument .removeFromSelection
- Unselects the content between given positions. The rest of selection is not touched.
Geometry
[
AT needs to draw outline for a range between two points (positions). No other use cases I'm aware of, so it might be better to provide 'outline' method like
partial interface AccessibleDocument {
void outline(AccessiblePos pos1, AccessiblePos pos2);
void clearOutlines();
};
It's tricky how to style that but maybe document or accessible should decide how it should be styled, for example, black outline on black background.
]
partial interface AccessiblePos {
readonly attribute Rect getBoundingRect(AccessiblePos pos);
readonly attribute sequence<Rect> getRects(AccessiblePos pos);
};
AccessiblePos .getBoundaringRect
- Returns a rect bounding the content enclosed by this and the given position.
AccessiblePos .getRects
- Returns a list of rects bounding the content enclosed by this and the given position.
Hit test
partial interface AccessibleDocument {
AccessiblePos positionFromPoint(int x, int y);
};
AccessibleDocument .positionFromPoint
- Returns accessible position at the given point in client coordinates.
[
- Hit testing on touch pads needs to find closest accessible in some local area. We may have strict (current implementation of modern APIs) and not strict modes. Mouse may need to work in not strict mode if operated by person with motor disability.
- Also it would need a history of movement (previous coordinates) to make right decision. If you move from up to down then you want to pick up next element vs previous one.
- We might need to have scanning feature, from left/right/up/down to right/left/down/up and semantic scanning from start to end.
].
Events
The accessible element may fire various number of events. You can add event listener on document accessible
interface AccessibleEvent {
readonly attribute DOMString type;
readonly attribute AccessibleElement target;
};
callback AccessibleEventListener (AccessibleEvent event);
partial interface AccessibleDocument {
void addEventListener(AccessibleEventListener listener);
void removeEventListener(AccessibleEventListener listener);
};
AccessibleDocument .addEventListener
- Adds event listener.
AccessibleDocument .removeEventListener
- Removes event listener.
Event types
"focus"
- Fired when accessible element is focused.
[section needs to be completed]
Patterns
Controls may expose own patterns to expose their semantics if it cannot be expressed by accessible element properties.
Tables
A cell of tables and grids provide the interface for 2 dimensional navigation. Cell headers can be obtained by labelledby relation.
interface AccessibleTableCell {
readonly attribute AccessibleElement? table;
readonly attribute unsigned long rowIndex;
readonly attribute unsigned long colIndex;
readonly attribute unsigned long rowSpan;
readonly attribute unsigned long colSpan;
};
AccessibleTableCell .table
- Return table accessible element for the cell.
AccessibleTableCell .rowIndex
- Return row index of the cell.
AccessibleTableCell .colIndex
- Return column index of the cell.
AccessibleTableCell .rowSpan
- Return number of rows that the cell is spanned to.
AccessibleTableCell .colSpan
- Return number of columns that the cell is spanned to.
JS accessible tree
You can tweak the accessible tree right in JS. You can do
- extend native semantics of the DOM element by overridding its accessible properties,
- create whole accessible trees for inaccessible content, for example, for graphic drawn in canvas.
Extending the semantics
You can extend native semantic of a DOM element by connecting it to an accessible source, the object describing accessible element. If the DOM node is not accessible then setting the accessible source on it makes it accessible. Otherwise the given source lands on top of the existing accessible object.
partial interface Node {
attribute AccessilbeSource? accessibleSource;
};
If the DOM node is accessible then you can provide accessible source by setting it up right in AccessibleElement.
partial interface AccessibleElement {
attribute AccessilbeSource? source;
};
The source object used to describe the accessible element implements AccessibleSource interface which basically portrays properties of AccessibleElement interface.
callback interface AccessilbeSource {
readonly attribute DOMString role;
readonly attribute sequence<DOMString> states;
readonly attribute DOMString name;
readonly attribute DOMString description;
readonly attribute DOMString value;
readonly attribute Object attributes;
readonly attribute Object relations;
readonly attribute sequence<DOMString> actions;
void activate(DOMString action);
AccessibleElement? element;
};
AccessibleSource .role
AccessibleSource .states
AccessibleSource .name
AccessibleSource .description
AccessibleSource .value
- These properties corresponds to properties of AccessibleElement interface.
AccessibleSource .attributes
- A map of attribute names and their values applied to the accessible element
AccessibleSource .relations
- A map of relation types and relations for this accessible element. The bunch of types may be used to point out related elements. You can use either Node, AccessibleElement or AccessibleSource objects. In latter case the object has to be in the accessible tree, in other words, it has to have associated accessible element when relation is poked by AT.
AccessibleSource .actions
- Return all supported actions on the accessible element.
AccessibleSource .activate
- Invoke the given action.
- Parameters
- action
- the action name to invoke
AccessibleSource .element
- Set by the browser when the accessible source is attached to the accessible element.
For example, if the author wants to override accessible name of the button
<button id="btn">press me</button>
then he can do this either with ARIA
<script>
document.getElementById("btn").setAttribute("aria-label", "new name");
</script>
or by setting its accessible source
<script>
document.getElementById("btn").accessibleSource = { name: "new name"; }
</script>
These are two equivalent approaches that can be used same time and mixed with each other the way that better fits the author's intent. It's important to keep in mind though that the accessible source has a priority over ARIA attributes. However setting the accessible source doesn't operate on DOM and thus it's supposed to be more performant. Another benefit of setting the accessible source is code readability since the author can set multiple properties at once in declarative way. For example,
<script>
document.getElementById("btn").accessibleSource = {
name: "new name",
description: "new description",
states: "disabled"
};
</script>
The accessible source can be also used when the author wants to add semantics to semantically neutral element. For example, making a listbox from DIV element may look this way:
<label id="label">Names:</label>
<div id="list"></div>
<script>
var listboxSource = {
role: "listbox",
relations: {
"labelledby": [ document.getElementById("label") ]
}
};
var listboxNode = document.getElementById("list");
listboxNode.accessibleSource = listboxSource;
</script>
It may look bulkier than its ARIA equivalent but it has own benefits outlined below
var listboxNode = document.getElementById("list");
listboxNode.setAttribute("role", "listbox");
listboxNode.setAttribute("aria-labelledby", "label");
It's worth to notice that accessible source approach like ARIA doesn't require the author to provide comprehensive description, i.e. correlating properties may be skipped. For example, presented labelledby relation makes unnecessary to provide accessible name. The browser should handle this on its own.
AccessibleSource interface has WEBIDL callback interface form which allows to implement lazy computation for accessible properties. An accessible property whose value isn't changed during whole life cycle of its accessible element or which doesn't require an accessible event on its change is good candidate for lazy computation. For example:
var listboxSource = {
role: "listbox",
relations: {
get labelledby() {
return document.querySelectorAll("*[listboxlabel]");
}
}
};
In this case the labelledby relation getter is more powerful than its ARIA version and all computations are running iff somebody inquired the relation.
Updating the source
An accessible source property change may require to notify the browser. The browser will fire accessible events and update its cache if necessary. Decision when to notify the browser or not should be based on accessible events model. In other words if the property change in native markup causes an accessible event then same change in accessible source requires it too.
partial interface AccessibleElement {
void sourceChanged(DOMString prop, optional any value);
};
AccessibleElement .sourceChanged
- Called when source has been changed to notify the host accessible element about property change. The browser is responsible to fire proper accessible events and update its internal representation.
- Parameters
- prop
- Name of the property like role or state
- value
- Value of changed property, used for complex properties like states. In this case it points to the changed state.
If the above listbox gets disabled then you have to reflect that in its state:
listboxSource.states = "disabled";
listboxNode.accesibleElement.sourceChanged("state", "disabled");
Building the accessible tree
The web developer can create whole portions of accessible content having no underlying DOM nodes. This can be done by creating AccessibleSource object and attaching it to the existing accessible tree.
typedef AccessibleSource or AccessibleTextSource or
sequence<AccessibleSource> or sequence<AccessibleTextSource>
AccessibleChild;
partial interface AccessibleElement {
AccessibleChild appendChildren(AccessibleChild);
void removeChildren(AccessibleChild);
AccessibleChild insertBefore(AccessibleChild);
AccessibleChild insertAfter(AccessibleChild);
};
AccessibleElement .appendChildren
- Creates accessible elements from given sources and appends them as children of the accessible element.
AccessibleElement .removeChildren
- Removes accessible elements, generated from given source elements, from the accessible tree.
AccessibleElement .insertBefore
AccessibleElement .insertAfter
- Creates accessible elements from given sources and inserts them as siblings of the accessible element.
Also you can manage AccessibleElement.source property to change the sourced accessible element (which is equivalent to removal/insertion operation) or remove the accessible element if its source is nullified.
Now you can add list item accessible elements to listbox accessible element.
var sourceItems = [
{
role: "listitem",
states: "focusable",
name: "item1"
},
{
role: "listitem",
states: "focusable",
name: "item2"
}
];
var listItems = listboxNode.accessibleElement.appendChildren(items);
Accessible text
interface AccessibleTextSource
{
readonly attribute DOMString text;
readonly sequence<DOMString> textAttributesAt(offset);
}
AccessibleTextSource .text
- Return the text.
AccessibleTextSource .textAttributesAt
- Returns text attributes at given offset within the text.
Canvas hit regions
Canvas hit region options are extended by accessibleElement which is used to connect accessible element with hit region. Whenever hit region is activated then activate method of connected AccessibleSource object is called with no argument provided.
Say you have a canvas with drawn button. In order to make it accessible you can create accessible button right in JS.
var canvasNode = document.getElementById("canvas");
var buttonSource = {
role: "button", name: "test",
actions: [ "click" ],
activate: function(aName) {
if (aName == "click") {
var event = new MouseEvent("click", { "clientX, this.x, "clientY": this.y });
canvasNode.dispatchEvent(event);
return;
}
// the button was activated by the user (called as callback from hit region)
this.button.propChanged("states");
this.states += "focused";
}
x: 100,
y: 100;
canvasNode: canvasNode,
button: null
};
buttonSource.button = canvas.accessibleElement.attachSource(buttonSource);
canvasNode.addHitRegion({ "accessibleElement": buttonSource });
If accessible button property is changed then you have to call sourceChanged, for example, if name is changed then you do
buttonSource.name = "new name";
button.propChanged("name");
Taxonomies
You can get hierarchical relations between roles, states, actions or anything else that has a taxonomy. For example, if the web author introduces a role 'x-checklistitem' which is compound from two roles 'checkbox' and 'listitem', i.e it inherits properties of both of them then the web author should integrate the new role into existing role hierarchy. Taxonomies are scoped by AccessibleDocument which provides bunch of methods to manage them.
partial interface AccessibleDocument {
Taxon? taxonOf(in DOMString taxonomy, in DOMString name);
void addTaxon(in DOMString type, in DOMString newbie, in sequence<DOMString> base)
raises(DOMException);
Taxonomy getTaxonomy(DOMString name);
};
AccessibleDocument .taxonOf
- Returns a taxon for the given taxon name of requested taxonomy.
- Parameters
- taxonomy
- a taxonomy name
- name
- name of requested taxa like 'button' in case of 'role' taxonomy
AccessibleDocument .addTaxon
- Adds new item into taxonomy
- Parameters
- name
- a taxonomy name
- newbie
- a new item put in hierarchy
- base
- list of base items the new item is inherited from
AccessibleDocument .getTaxonomy
- Return an object describing requested taxonomy.
- Parameters
- name
- a taxonomy name
interface Taxon {
stringifier readonly attribute DOMString name;
readonly attribute DOMString description;
readonly attribute sequence<Taxon> parentTaxa;
readonly attribute sequence<Taxon> childTaxa;
bool is(DOMString or Taxon base);
};
Taxon .name
- Return name of the taxa
Taxon .description
- Return localized description of the taxa
Taxon .parentTaxa
- Return list of taxa the taxon is inherited from.
Taxon .childTaxa
- Return list of taxa that is inherited from this taxon.
Taxon .is
- Return true if given taxon name is in base of the taxon.
interface Taxonomy {
Taxon? getTaxon(DOMString name);
readonly attribute sequence<Taxon> rootTaxa;
void addTaxon(DOMString name, sequence<Taxon or DOMString> baseTaxa);
void removeTaxon(Taxon taxon);
};
Taxonomy .getTaxon
- Return a taxon object for the given name.
- Parameters
- name
- taxon name
Taxonomy .rootTaxa
- Return list of base taxa
Taxonomy .addTaxon
- Adds new taxon into the taxonomy.
- Parameters
- name
- a new taxon name
- baseTaxa
- a list of taxa the taxon is inherited from
Taxonomy .removeTaxon
- Remove the given taxon and all taxa inherited from it from the taxonomy.
- Parameters
- taxon
- taxon object
To add a new role into role hierarchy:
a11ydoc.addTaxon("role", "x-checklistitem", [ "checkbox", "menuitem" ]);
and then AT can figure out what new x-checklistitem is
var taxon = a11ydoc.taxonOf("role", "x-checklistitem");
if (taxon.is("menuitem")) {
// process menuitem selection
}
HTML and beyond
This doc introduces common patterns to express the semantics of markup languages to accessibility. Markup specifics is not a target for this doc in general. Each markup specification has to take care to describe their accessibility stuff in terms of this API.
Extensibility
The web application might need to extend default taxonomies to express the new semantics. For example, the web service publishing music sheets can introduce new characteristics like role, states, etc to describe music sheet content. However the web application should take care to explain new characteristic by extending default taxonomies, i.e. by describing the connection between old and new characteristics. That will resolve any backward compatibility issue, so if the consumer doesn't know about new roles then he can still figure out a closest match it's aware about. For example, if the web app author introduces "x-redbutton' and provides a role taxonomy for it saying this is an extension of 'button' role, then the consumer unfamiliar with 'x-redbutton' role will treat it as a button.
The author should follow name convention to avoid potential collisions with future additions into the spec predefined lists. Thus all names should be prefixed by 'x-' like 'x-redbutton' from example above.
Music sheet example
To make a music sheet accessible the web app may introduce bunch of new roles, attributes and relations:
roles: 'sheet' - inherited from 'section' 'note' - inherited from 'image', describes the note
role 'sheet' attributes: instrument: DOMString, tempo: number/DOMString clef: DOMString
role 'note' attributes:
key: enum { C, D, E, F, G, A, H },
alteration: enum { none, flat, sharp },
octave: enum { ... },
duration: number,
effects: sequence<DOMString>, // tremolo, bend
role 'note' relations: crescendo: [note, ...] a list a notes the crescendo is applied to diminuendo: [note, ...] a list a notes the diminuendo is applied to