Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: aria-actions addition to the ARIA spec #1805

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions core-aam/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5711,6 +5711,62 @@ <h4>Not Mapped</h4>
>. Its absence not only indicates that the accessible object is not grabbed, but further defines it as not grab-able. These cases are marked as "Not mapped" without an asterisk.
</p>
</section>
<h4 id="ariaActions"><code>aria-actions</code></h4>
<table aria-labelledby="ariaActions">
<tbody>
<tr>
<th>ARIA Specification</th>
<td>
<a class="property-reference" href="#aria-actions"><code>aria-actions</code></a>
</td>
</tr>
<tr>
<th>MSAA + IAccessible2</th>
<td>
<span class="property">
Interface: <code>IAccessibleAction</code>, adding one action for each accessible node referenced by `aria-actions` that is exposed in the accessibility tree. The interface methods
are mapped as follows:
<ul>
<li>`name`: return the DOM id of the corresponding target node.</li>
<li>`localizedName`: return the corresponding target node's accessible name.</li>
<li>`doAction`: behave as if `doAction(0)` were called on the corresponding target accessible node.</li>
</ul> </span
><br />
<span class="property">Object Attribute: <code>aria-actions</code> &lt;value&gt;</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to expose this? We don't normally expose idrefs in object attributes. IAccessibleAction should be sufficient.

</td>
</tr>
<tr>
<th><abbr title="User Interface Automation">UIA</abbr></th>
<td>
<span class="property"
smhigley marked this conversation as resolved.
Show resolved Hide resolved
>Custom Property: <code>AccessibleActions</code>: element array of accessible nodes matching IDREFs, if the referenced objects are in the accessibility tree</span
smhigley marked this conversation as resolved.
Show resolved Hide resolved
><br />
<span class="property">Object Attribute: <code>aria-actions</code> &lt;value&gt;</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UIA doesn't have object attributes. Perhaps you meant this?

Suggested change
<span class="property">Object Attribute: <code>aria-actions</code> &lt;value&gt;</span>
<span class="property">Property: <code>AriaProperties.actions</code>: &lt;value&gt;</span>

That said, I don't quite follow why we're exposing this. See above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yup, I meant to do Property: AriaProperties.actions: <value>, thanks for catching that!

It came up in discussions with NVDA & JAWS -- it's definitely not as important as the IAccessibleActions pattern or the AccessibleActions custom property, but folks on those calls said it could be useful as a quick way to check if actions are intended to exist for a given node.

I'm not super opinionated on this one, and we can bring it up again to ask if it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In conversations today I remembered another reason for this -- it should in theory allow for the possibility to query whether an element has actions even when those actions are not present in the DOM.

I think the issue with that approach is that authors also might not pass in id values when the elements they refer to are not rendered. I'm now wondering if we should treat the presence of the aria-actions attribute, even if set to the empty string, as meaningful or indicative of an element supporting actions even if they are not currently rendered.

WDYT? In theory perhaps the interface mapping with an empty array could also indicate that, and maybe this whole idea is too over-engineered 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we definitely need to expose the fact that actions exist when they aren't currently rendered?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. What can a screen reader user begin with emty actions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MarioBatusic the idea is that the actions will exist when the control gains focus (or hover, usually), but may not exist before then. If a screen reader user is browsing with virtual cursor and doesn't have focus following the cursor, this mapping would make it possible to still let them know that actions exist. If they attempt to interact with those actions, the screen reader would focus on the control, thus presumably causing the actions to render.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcsteh I updated the mapping to be an empty string rather than the value, as we discussed at TPAC. WDYT?

Also if anyone's opinions change on whether this is a valuable thing to have, let me know. I'm still a bit ambivalent (mostly worried that aria-actions with invalid ids will still get exposed, but nothing will show up even after focusing the control)

</td>
</tr>
<tr>
<th><abbr title="Accessibility Toolkit">ATK</abbr>/<abbr title="Assistive Technology-Service Provider Interface">AT-SPI</abbr></th>
<td>
<span class="relation">
Interface: <code>AtkAction</code>, adding one action for each accessible node referenced by `aria-actions` that is exposed in the accessibility tree. The instance methods are
mapped as follows:
<ul>
<li>`get_n_actions`: return the number of accessible nodes referenced by `aria-actions` that are exposed in the accessibility tree</li>
<li>`get_name`: return the non-localized string 'click'.</li>
<li>`get_localized_name`: return the corresponding target node's accessible name.</li>
<li>`do_action`: behave as if `do_action` were called on the corresponding target accessible node.</li>
</ul>
</span>
</td>
</tr>
<tr>
<th><abbr title="macOS Accessibility Protocol">AX API</abbr></th>
<td>
<span class="property">Implement as <a href="https://developer.apple.com/documentation/uikit/uiaccessibilitycustomaction">custom action</a></span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it, UIAccessibilityCustomAction (as linked here) can only be used with Mac Catalyst. Browsers aren't Mac Catalyst apps, so I don't think they can use this. We need the equivalent for the protocol used by Mac desktop apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tagging in @cookiecrook for this one, since he wrote the wording for it 😊

</td>
</tr>
</tbody>
</table>
<h4 id="ariaActiveDescendant"><code>aria-activedescendant</code></h4>
<table aria-labelledby="ariaActiveDescendant">
<tbody>
Expand Down
62 changes: 58 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ <h3>Information for Authors</h3>
<li><rref>treegrid</rref></li>
<li><rref>tablist</rref></li>
</ul>
<p>User agents that support <abbr title="Accessible Rich Internet Application">WAI-ARIA</abbr> expand the usage of host language mechanisms such as <code>tabindex</code>, <code>focus</code>, and <code>blur</code> to allow them on all [=elements=]. Where the host language supports it, authors MAY add any element such as a <code>div</code>, <code>span</code>, or <code>img</code> to the default tab order by setting <code>tabindex=&quot;0&quot;</code>. In addition, any item with <code>tabindex</code> equal to a negative integer is focusable via script or a mouse click, but is not part of the default tab order. This is supported in both [[HTML]] and [[SVG2]].</p>
<p>Authors MUST also ensure any elements referenced by <pref>aria-actions</pref> are either in the tab order, accessible through managed focus as part of a composite widget container, or have a documented alternative method to achieve their function while focus is on the referencing element.</p>
<p>User agents that support <abbr title="Accessible Rich Internet Application">WAI-ARIA</abbr> expand the usage of host language mechanisms such as <code>tabindex</code>, <code>focus</code>, and <code>blur</code> to allow them on all [=elements=]. Where the host language supports it, authors MAY add any element such as a <code>div</code>, <code>span</code>, or <code>img</code> to the default tab order by setting <code>tabindex=&quot;0&quot;</code>. In addition, any item with <code>tabindex</code> equal to a negative integer is focusable via script or a mouse click, but is not part of the default tab order. This is supported in both [[HTML]] and [[SVG2]].</p>
<p>Authors MAY use <pref>aria-activedescendant</pref> to inform <a>assistive technologies</a> which descendant of a <rref>widget</rref> element is treated as having keyboard focus in the user interface if the role of the widget element supports <code>aria-activedescendant</code>.
This is often a more convenient way of providing keyboard navigation within widgets, such as a <rref>listbox</rref>, where the widget occupies only one stop in the page <kbd>Tab</kbd> sequence and other keys, typically arrow keys, are used to focus elements inside the widget.</p>
<p>Typically, the author will use host language <a>semantics</a> to put the widget in the <kbd>Tab</kbd> sequence (e.g., <code>tabindex="0"</code> in HTML) and <code>aria-activedescendant</code> to point to the ID of the currently active descendant. The author, not the user agent, is responsible for styling the currently active descendant to show it has keyboard focus. The author cannot use <code>:<span class="css-selector">focus</span></code> to style the currently active descendant since the actual focus is on the container.</p>
Expand Down Expand Up @@ -1620,6 +1621,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -1909,6 +1911,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -2666,6 +2669,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -2755,6 +2759,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -3083,6 +3088,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -3427,6 +3433,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-brailleroledescription</pref></li>
<li><pref>aria-label</pref></li>
Expand Down Expand Up @@ -4067,6 +4074,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -4778,6 +4786,7 @@ <h2>Definition of Roles</h2>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -6213,6 +6222,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -8013,6 +8023,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -8172,6 +8183,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -8276,9 +8288,10 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
</ul>
</td>
</tr>
Expand Down Expand Up @@ -8364,6 +8377,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -8921,6 +8935,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -9243,6 +9258,7 @@ <h5>Presentational Role Inheritance</h5>
<th class="role-disallowed-head" scope="row">Prohibited States and Properties:</th>
<td class="role-disallowed">
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-braillelabel</pref></li>
<li><pref>aria-label</pref></li>
<li><pref>aria-labelledby</pref></li>
Expand Down Expand Up @@ -10212,6 +10228,7 @@ <h3>Drag-and-Drop Attributes</h3>
<h3>Relationship Attributes</h3>
<p>This section lists [=attributes=] that indicate <a>relationships</a> or associations between [=elements=] which cannot be readily determined from the document structure.</p>
<ul>
<li><pref>aria-actions</pref></li>
<li><pref>aria-activedescendant</pref></li>
<li><pref>aria-colcount</pref></li>
<li><pref>aria-colindex</pref></li>
Expand Down Expand Up @@ -10241,6 +10258,43 @@ <h3>State change notification</h3>
<h2>Definitions of States and Properties (all aria-* attributes)</h2>
<p>Below is an alphabetical list of <abbr title="Accessible Rich Internet Applications">WAI-ARIA</abbr> <a>states</a> and [=ARIA/properties=] to be used by authors. A detailed definition of each <abbr title="Accessible Rich Internet Applications">WAI-ARIA</abbr> state and [=ARIA/property=] follows this compact list.</p>
<p id="index_state_prop" class="placeholder">Placeholder for index of states and properties</p>
<div class="property" id="aria-actions">
<pdef>aria-actions</pdef>
<div class="property-description">
<p><a>Identifies</a> a related element or elements whose primary activation (<code>click</code> event) will trigger a behavior or operation relevant to the referencing user interface object, such as the close button for a dialog, or a reply button relevant to the email message that references it.</p>
<p>The <code>aria-actions</code> attribute allows an element to reference other interactive elements which trigger actions related to the referencing element. For example, in a web mail application, the selected email message could reference buttons that perform actions on that email, such as Reply, Forward, and Delete. Actions are triggered by pointer events (click, tap) that can be simulated by assistive technologies.</p>
<ul>
<li>Authors MUST ensure that elements referenced by <code>aria-actions</code> have an accessible name.</li>
<li>Authors MUST ensure that related action elements respond to a <code>click</code> event and are not limited to modality-specific activation such as the <code>keyup</code> or <code>touchend</code> events.</li>
<li>Authors MUST ensure each referenced action element is either directly navigable with the keyboard or that there is a keyboard shortcut to directly activate it when focus is on the referencing element.</li>
<li>Authors SHOULD ensure that related actions elements are visible and activatable when the current element is focused by the user agent or assistive technology.</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a "SHOULD" instead of a "MUST"?

The "focus" aspect of this requirement should specifically address active descendant in a way that is consistent with how we do so elsewhere.

Suggested change
<li>Authors SHOULD ensure that related actions elements are visible and activatable when the current element is focused by the user agent or assistive technology.</li>
<li>Authors SHOULD ensure that elements referenced by <code>aria-actions</code> are visible and activatable when the referencing element is focused or when the referencing element is referenced by <sref>aria-activedescendant</sref> from an element that has DOM focus.</li>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a "SHOULD" instead of a "MUST"?

Mainly because an author cannot detect all forms of AT focus, so there is no way to achieve a MUST. But also to avoid overly restrictive UI flexibility. Authors may have good reasons for not doing this, so we should not penalize them with a validation error.

Since you addressed the active descendant in a later thread, let's continue that discussion there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: Matt's suggestion of

+ or when the referencing element is referenced by <sref>aria-activedescendant</sref> from an element that has DOM focus.

IMO, this change will reduce the clarity of the intention and complicate the implementation... In this scenario, it's okay for the container element to manage these actions rather than the activedescendant. Chaining an action through an activedescendant has no precedence on any platform I'm aware of.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cookiecrook wrote:

Mainly because an author cannot detect all forms of AT focus, so there is no way to achieve a MUST. But also to avoid overly restrictive UI flexibility.

Authors should not be able to detect the presence of AT or any form of AT focus. I think we should remove the phrase "focused by assistive technology" from the definition. AFAIK, we have not defined assistive technology focus and do not use it elsewhere in the spec.

I don't quite understand how this would work without mapping actions to elements referenced by aria-activedescendant. Consider a listbox or tree implemented with aria-activedescendant. If there is going to be a custom action on each option or treeitem, the option or treeitem will not have DOM focus; it will be referenced by aria-activedescendant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a "SHOULD" instead of a "MUST"?

Mainly because an author cannot detect all forms of AT focus, so there is no way to achieve a MUST. But also to avoid overly restrictive UI flexibility. Authors may have good reasons for not doing this, so we should not penalize them with a validation error.

I think there are some potential author musts here.
If the focus is from the UA, then any aria-actions must be visible.
In fact, if the element with aria-actions is visible, then all of it's aria-actions elements must also be visible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would folks generally be in favor of removing "or assistive technology" and make it a "MUST", making the entire sentence read as follows?:

Authors MUST ensure that related actions elements are visible and activatable when the current element is focused by the user agent.

<li>User Agents SHOULD use the accessible names of elements referenced by <code>aria-actions</code> to determine the names of actions that are exposed in a platform accessibility API.</li>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have some "User Agents MUST"s here? Aaron and I were thinking something along the lines of "User Agents must not expose aria-actions if the Author MUSTs are not followed".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, I can see why that would be helpful. Do you have suggestions for specific exceptions? The first one that comes to mind is not exposing actions if the element is in focus, but the action ids do not point to an element.

</ul>
</div>
<table class="property-features">
<caption>Characteristics:</caption>
<thead>
<tr>
<th scope="col">Characteristic</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th class="property-applicability-head" scope="row">Used in Roles:</th>
<td class="property-applicability">All elements of the base markup except for some roles or elements that prohibit its use</td>
</tr>
<tr>
<th class="property-descendants-head" scope="row">Inherits into Roles:</th>
<td class="property-descendants">Placeholder</td>
</tr>
<tr>
<th class="property-value-head" scope="row">Value:</th>
<td class="property-value"><a href="#valuetype_idref_list">ID reference list</a></td>
</tr>
</tbody>
</table>
</div>
<div class="property" id="aria-activedescendant">
<pdef>aria-activedescendant</pdef>
<div class="property-description">
Expand Down
Loading