w3c / aria

Accessible Rich Internet Applications (WAI-ARIA)
https://w3c.github.io/aria/
Other
654 stars 125 forks source link

AccName Role Traversal Proposal #1821

Open accdc opened 2 years ago

accdc commented 2 years ago

As we spoke of during the last ARIA meeting, there needs to be a way of defining synchronicity between the ARIA spec and the AccName spec regarding which roles are recursively traversed.

Since the time when this topic was first discussed, reference https://lists.w3.org/Archives/Public/public-aria/2017Jun/0057.html This level of definition was never added to the ARIA spec, even though browsers such as Chrome and others already have logic for this built into their AccName implementations.

As a result of this, it was exceedingly difficult to find a way of adding this to the AccName spec because there was no supported grouping mechanism within the ARIA spec to validate implementation. Unofficially, whether or not a role supported “name from content” was referenced instead for this logic within the ARIA spec within step 2H by referring back to step 2F, but this has led to additional confusion because there are times when roles must be traversed even if they don’t support name from content, and there is no way of definitively saying it should be one way or another given the lack of definition within the ARIA spec. This leads to additional disagreement and makes it impossible to achieve interoperability, which is what the specs strive to achieve.

Since the current grouping mechanism of whether or not a role supports “name from content” is not sufficient for this purpose, a new mechanism needs to be added that can provide this additional level of specificity. This will make it possible to easily add logic to the AccName algorithm to refer back to this grouping mechanism within the ARIA spec to identify which role should or should not be traversed and under which circumstances.

Currently, at the end of each role section in the ARIA spec is a table that defines whether that role supports “name from content”.

I propose to add a new row to that same table that defines whether the role is “Traversable”.

Traversable would support 3 values, “Always”, “Never”, and “When Focusable”.

The word “Traversable” would need to be added to the Terms list within the ARIA spec to specify its purpose. E.G. To specify whether or not the content of a role is traversable within the AccName computation and under which circumstances. (Or whatever…)

These three values (Always, Never, and When Focusable) match the functional criteria that is built into the browsers based on prior agreements when this was discussed in the past.

The meanings of Always and Never are clear in context, though it is important to note that the value When Focusable refers to the circumstance when either the role or one of its ancestor elements is focusable. This definition is not the same as saying that the role is traversable only when the role receives focus. This definition is meant to define that it will be traversable in all cases when the role or one of its ancestor elements is focusable regardless where focus resides.

Officially defining these within the ARIA spec will remove any ambiguity and ensure future interoperability as each spec evolves.

The following role list mostly matches what was originally discussed for this purpose, which includes updates to the ARIA spec and relevant guidance since then. It can be modified as needed to ensure agreement between implementors.

Always Traverse

button, checkbox, heading, link, menuitem, menuitemcheckbox, menuitemradio, option, radio, slider, switch, tab, tooltip, treeitem

Never Traverse

alert, alertdialog, application, article, banner, complementary, dialog, document, feed, figure, form, grid, img, listbox, log, main, marquee, math, menu, menubar, navigation, radiogroup, region, search, searchbox, separator, tablist, tabpanel, textbox, timer, toolbar, tree, treegrid

Traverse Only When Focusable

cell, columnheader, combobox, gridcell, contentinfo, definition, directory, group, list, note, row, rowgroup, rowheader, scrollbar, spinbutton, status, table, term

As noted, the above lists are not set in stone and we can hammer out which ones we can all agree on. (I may have overlooked a role so please suggest where to add if so.)

After this is added to the ARIA spec, it will then be possible to add a relatively simple step to the AccName algorithm to handle cases of whether or not a role is traversable and refer back to whether it is either “Always”, “Never”, or only “When Focusable”.

Feedback is welcome.

All the best, Bryan

scottaohara commented 2 years ago

appreciate you outlining all this, as it's giving me something to wrap my head around in a much easier way. Though with that said, in regards to the above i'm unclear what would happen with the following use case i mentioned in the our last WG meeting

<a href=...>
  <article>
     content goes here
  </article>
</a>

i see article is mentioned as never traversable, so, would this not still produce a link without an accessible name? if so, maybe there needs to be another designation? Unless "only when focusable" doesn't necessarily refer just to the element, if focusable, but also a parent/ancestor element that would be nameless otherwise?

I realize you said that the lists are not set in stone, but yeh, i'm not sure i understand how some roles like banner or article are in one category, but contentinfo and group are in another.

Thanks again for the work on this Bryan. Regardless of my questions/comments above, i'm sure this'll help move us forward.

accdc commented 2 years ago

Actually article was part of the list originally proposed by Aaron back in 2017, which is what I am basing this list on mainly since I don't have time to take the Chrome code apart to see what it's doing.

If others in the group agree to move this into the When Focusable category, I will abide by whatever people can agree on. The same goes with the rest of the roles.

jnurthen commented 2 years ago

Where is generic in this list?

accdc commented 2 years ago

That's one I forgot, generic should be in the Always category.

jnurthen commented 2 years ago

The third category in Chrome is: // ----- Conditional: contribute to ancestor only, unless focusable ------- // Some objects can contribute their contents to ancestor names, but // only have their own name if they are focusable Why the deviation from this in this proposal?

accdc commented 2 years ago

I was mainly thinking of screen readers like JAWS that use a virtual buffer model that does not follow focus.

If this were changed to only when it receives focus, it would break Scott's example in all cases such as when pressing Insert+F7 to get a list of all links on the page.

jnurthen commented 2 years ago

So there are 2 parts to this. Roles that can have their own accessible name and roles that can only contribute to an ancestor's accessible name (for 2F). We need to cover both parts of this.

accdc commented 2 years ago

"So there are 2 parts to this. Roles that can have their own accessible name and roles that can only contribute to an ancestor's accessible name (for 2F)."

As far as I know this is already covered. "Name from author" already specifies if a role is namable, "Name from content" specifies if it can receive a general name using it's content, and "Traversable" would only specify if it can be traversed in 2F.

jnurthen commented 2 years ago

In case it helps I exported https://searchfox.org/mozilla-central/source/accessible/base/RoleMap.h#1 into a google sheet at https://docs.google.com/spreadsheets/d/1iLk-KTX2PZvqIhdhQzM9vfMI3za3RlWQCRNx1e6m2VM/edit?usp=sharing The last column contains the mozilla accessible name categorization for each role which is one of the following:

  1. eNameFromSubtreeIfReqRule
  2. eNameFromSubtreeRule
  3. eNameFromValueRule
  4. eNoNameRule

I could look through the code to work out exactly what each of these means but maybe @jcsteh can fill this in.

jcsteh commented 2 years ago

The last column contains the mozilla accessible name categorization for each role which is one of the following:

1. eNameFromSubtreeIfReqRule

This effectively maps to Name from: author, Traversable: always.

2. eNameFromSubtreeRule

This effectively maps to Name from: content, Traversable: always.

3. eNameFromValueRule

This deals with 2C of the AccName spec:

Otherwise, if the current node is a control embedded within the label (e.g. any element directly referenced by aria-labelledby) for another widget, where the user can adjust the embedded control's value, then return the embedded control as part of the text alternative...

4. eNoNameRule

This effectively maps to Name from: author (or prohibited), Traversable: never.

it is important to note that the value When Focusable refers to the circumstance when either the role or one of its ancestor elements is focusable.

Note that Gecko does not currently support this concept at all. Overall, I think it makes sense. However, one problem is that "focusable" can mean a few different things:

  1. The element is tabbable. Traversing definitely makes sense in this case.
  2. The element is explicitly focusable but not tabbable; i.e. tabindex="-1". It probably makes sense to traverse in this case, but this gets muddy when combined with 3) below.
  3. The element is scrollable. In Gecko, this means that it will be tabbable so that a keyboard user can scroll the element. In Chromium, this currently isn't the case, though I heard that this might change in future. It probably doesn't make sense to traverse in this case, as that could result in ridiculously long names for large containers. To make things more complicated, some authors apply tabindex="-1" to prevent this from being tabbable, at which point 2) applies and it still probably doesn't make sense to traverse for the same reason.
jcsteh commented 2 years ago

FWIW, I don't think we could reasonably support Traversable: when focusable in Gecko until this issue is sorted out.

accdc commented 2 years ago

Since this is simply a proposal, I expected some shifting based on backend limitations. If "When Focusable" were changed to "When Focused To", would you be able to support it?

JAWS-test commented 2 years ago

I think the problem is more complex and the following things should be clarified in AccName:

However, the rules outlined here are probably too complex, on the one hand, to implement consistently in browsers and, on the other hand, for web developers to apply them correctly. It would be much simpler to abandon the concept that an element cannot have an AccName and say that the text content is always the AccName (for elements that do not have Name from Author, like a div).

jcsteh commented 2 years ago

If "When Focusable" were changed to "When Focused To", would you be able to support it?

It would be easier to support, but I don't think it's what we want. Whether something is focused is a lot more transient than whether something is focusable. Thus, the name would potentially change quite often, requiring a lot of extra name change events.

On further reflection, I also realised I misunderstood the purpose of this "when focusable" rule. If I'm reading your proposal correctly, you're suggesting this only relates to aria-labelledby/describedby traversals and not "Name from: content". I'm curious as to why we want something to be traversed when focusable for aria-labelledby/describedby, but not not when calculating name from content. For aria-labelledby/describedby, I would've thought they should just always be traversed for those roles, whereas we probably do want them to be included in name from content only when focusable.

accdc commented 2 years ago

I don't know what to do with this then.

This is not just for aria-labelledby and aria-describedby traversals, but for any child node traversal that involves step 2H.

We have to find a way of documenting how this is implemented in the browsers, because currently the only thing the spec says is to recursively start at 2F.i when traversing all child nodes, and this will not work correctly for all element and role types.

E.G. A heading is a simple example of why, for example if it includes an embedded menu that is rendered inline with a triggering element within the heading. Also a tree using the same concept, or a standard select element such as a country selector that has over 280 options within it.

If you apply 2H as it is currently written, skipping only to 2F.i, then name from content is skipped and everything must be included as the heading name regardless what type of role it is.

Is this really what you want to happen?

jcsteh commented 2 years ago

Let's put aside the precise definition of focusable, focused, etc. for a moment.

  1. I think having always traversable and never traversable makes sense. However, I think the focusable concept is only relevant when computing the name for the element itself, not for descendant traversal. That is, for aria-labelledby, aria-describedby and descendant traversal, we should only look at always traversable or never traversable.
  2. We should have an additional list of roles that do allow name from content when they're focusable. This is effectively a repair mechanism if the author didn't specify a name when they should have. Btw, it is this repair mechanism that Chrome currently implements but Firefox does not.
accdc commented 2 years ago

We spoke about this at the last ARIA meeting, and I think it would be beneficial for us to have a deep dive meeting to hash out what is or is not possible from the implementer perspective and the issues being addressed. Would you @jcsteh be willing to attend that? Ideally this would also include @aleventhal and @cookiecrook as well so we can work this out.

jcsteh commented 2 years ago

The main feasibility question regarding implementation relates to the definition of focusable. The rest all seems feasible once we flesh out the details. Regardless, I'd be happy to discuss this in a deep dive meeting if we can make the time zones play ball.

accdc commented 2 years ago

Thanks, that would help a lot for all of us to be on the same page.

Here are some possible dates and times for us to do this. I apologize in advance, this is California Pacific time where I live because I'm not sure of the time zones of everyone involved.

Thursday October 27: 4:00-5:00 PM PST. Thursday November 3: 4:00-5:00 PM PST. Thursday November 10: 4:00-5:00 PM PST.

Everyone in the WG is welcome, though these are the specific participants that I will need to attend for this to be productive. @jcsteh @cookiecrook @aleventhal @scottaohara

Can all of you please confirm which dates will work for your schedule?

MelSumner commented 2 years ago

Per Meeting Zone

A Thursday at 4:00 PM PST works out to be 6:00 PM CST, 7:00 PM for EST, and Friday 10 AM in Australia.

accdc commented 2 years ago

Awesome thanks!

@jcsteh , @aleventhal , @cookiecrook , @scottaohara This Thursday, for example, works great for me. (hint hint...) ;)

jcsteh commented 2 years ago

All of those dates work for me at this stage.

Just to clarify (largely as a note to self), it's 9AM Brisbane/Queensland time, since Queensland doesn't have DST but NSW/Vic do because silly reasons.

scottaohara commented 2 years ago

nov 4 or the 10th are fine by me

accdc commented 2 years ago

Hi @jcsteh It looks like we are all good to go for this Thursday (November 3) at 4:00PM California Pacific time. I think that would be 9:00AM on the 4th your time? I'll assume this works for you and ask @jnurthen if he can setup the meeting invite unless you say it won't work on your end. Hopefully talk soon! :)

jcsteh commented 2 years ago

That time works for me. Thanks.

spectranaut commented 2 years ago

This was discussed in a deep dive: https://www.w3.org/2022/11/03-aria-dive-minutes

jcsteh commented 2 years ago

During the deep dive call, I said I'd provide a summary of my proposal. Looking back at all the comments in this issue, I think my proposal is simply to take the original proposal made by @accdc in https://github.com/w3c/aria/issues/1821#issue-1396923028 except for the focusable part. That is, we add traversable: always and traversable: never, but not traversable: focusable. Because we're only applying this to recursive traversal (2H), whether the element is focusable shouldn't be relevant. Is there any reason this is problematic? I couldn't establish one on the call.

Of course, there's still a lot to debate as to which roles are traversable. I acknowledge the article inside link example, but making article traversable feels kinda nasty given that an article potentially suggests a great deal of content.

Separately, we probably do need a concept for roles which calculate name from content when they're focusable, but don't do this when not focusable. Chromium has this concept as noted in https://github.com/w3c/aria/issues/1821#issuecomment-1267721060, but Gecko currently doesn't. I'm not sure about WebKit. In any case, I think this should be handled separately from recursive traversal.

I also said I'd dig into the situation with group, which is listed as a role that Gecko traverses recursively but has a specific exception for treeitems. Group was added as a recursively traversable role in https://bugzilla.mozilla.org/show_bug.cgi?id=1616851 because Gmail messages are a cell containing a tbody with display: block which gets mapped to the group role (since it's a row group). That tbody contains details about the sender, etc., so it needed to be included. However, it looks like something in the markup or Gecko has changed there and there doesn't seem to be an intervening group any more, so maybe we can remove that anyway.

accdc commented 2 years ago

Thanks, I don't mind separating these out as you suggest. I'll flag this for discussion in the ARIA WG meeting.

One option for focusable elements is to traverse all child elements like so. (Solution applies to both names and descriptions when computed.)

  1. If the current node does not support "name from author", always traverse.
  2. If the current node supports "name from author" but has no explicit name, always traverse.
  3. If the current node supports "name from author" and has an explicit name, return only the explicit name and do not traverse.
jcsteh commented 2 years ago

One option for focusable elements is to traverse all child elements like so.

I assume you're suggesting this would apply when the element is the root of the traversal? This is in contrast to the separate definition of traversable roles, which only get traversed when recursing. I think this should work, though we still need to clarify the definition of focusable, which we should probably do separately. I'm still worried about scrollable areas getting huge names even though the author didn't intend them to be focusable.

accdc commented 2 years ago

@jcsteh I apologize that wasn't actually clear. I was only referring to an element that received focus in the same manner as when presentational elements are exposed when they receive focus and are not exposed otherwise.

So basically, we could break out the traversable rules for always and never as you mentioned, but then we would have a different rule for when an element receives focus and needs to be exposed in cases when there is no detectable name.

For example, if you had a link that had a valid aria-label on it, the content would not be traversed in accordance with what the AccName spec does right now because the name provided is valid.

However, if you have a link that includes structures that are not supposed to be traversed and there is no other valid mechanism present to provide a name, then all child structures would be traversed as a failsafe to ensure that a valid name would be provided anyway.

Would this make sense for handling elements that receive focus?

jcsteh commented 2 years ago

I think it perhaps makes sense to ignore the lack of "name from contents" for focusable elements. Outside of the traversable: always/never rule, I'm not so keen on changing descendant traversal for focusable elements:

  1. It complicates the algorithm and code significantly. We'd have to do a normal name computation, realise there was no name, then do another computation, this time ignoring all the rules.
  2. There are certain roles I don't think it ever makes sense to traverse. Say an author put a listbox with 15000 items inside a link. I don't think we should traverse into the listbox, regardless of whether the link is focusable. That might result in an unnamed link, yes, but I think there's only so far we can/should go to compensate for poor authoring.
JAWS-test commented 2 years ago

Why can't every element have an AccName? For some the AccName can be set by the author, for most the AccName can be the content, but either or both should always work. Incidentally, this is also how the screen readers work, which do not care whether AccName is forbidden. For example, in JAWS I can use Y to jump to the next div element and INS+CTRL+Y to see a list of all generic areas. Although a div cannot be labeled, JAWS uses the content (or the beginning of the textual content) as the label, which makes sense.

accdc commented 2 years ago

@jcsteh As a quick update, we discussed the latest traversal plan in the Nov 17 meeting. Minutes: https://www.w3.org/2022/11/17-aria-minutes#t05

We all agreed to remove and shelve the handling of focusable elements, and simply concentrate on building a list of Never Traverse and Always Traverse so we can find agreement on these roles for consistency.

The handling of what to do when an element receives focus and it does not have a valid name will be handled by the browser error correction algorithm, which will be up to each browser to implement. Part of the logic of doing this, is to move developers away from using bad markup within focusable elements, and to also prevent the AccName algorithm from becoming even more confusing than it already is.

So, as our next action item, we need to compile a role list for both categories of Never Traverse and Always Traverse, and (You'll love this part...) Since you seem to have the best grasp of which roles should or should not be within these categories already, you have been volunteered to draft the first list! Congrats! ;)

Once we have the role list proposal, we can then go through it within the rest of the WG to achieve consensus and then add it to the ARIA spec and AccName spec where needed.

Are you okay with drafting the first role lists for these 2 categories?

jcsteh commented 1 year ago

Sorry for the ridiculously slow response. I can certainly take a first stab at these lists, but I may not be able to get to it for at least a couple of weeks.

accdc commented 10 months ago

Hi, this issue came up again today as part of the AccName issue https://github.com/w3c/accname/issues/226

I'll agenda this so we can go over it again at the next ARIA meeting.

jcsteh commented 10 months ago

Okay. Over a year late - I'm really sorry - but better late than never I guess. 😳

As requested, I've taken a first stab at drafting these lists. I considered various parts of the spec and what browsers are currently doing, and garnished it with a bit of my own opinion. :) I was only able to look at Gecko and Chromium, so WebKit is missing from this analysis.

Gecko and Chromium currently disagree on a few of these:

joanmarie commented 10 months ago

it seems that @joanmarie very deliberately implemented this in Gecko, which makes me think there was a good reason to traverse these.

Looking at the code comment I wrote, and the date of the commit, there's a good chance the reason was to get AccName 1.1 to rec and we had a failing test case in Gecko. :smile: (Though my code comment mentions an example that I don't see in the spec, even in version 1.1.) :woman_shrugging:

That said, if an author put a table element inside a label element and provided no other name source, I would think that the displayed text should be the accessible name of the thing being labelled.

And that said, I don't have a strong opinion on this.

spectranaut commented 10 months ago

Discussed in today's meeting: https://www.w3.org/2024/02/01-aria-minutes.html#t06

scottaohara commented 10 months ago

the worry that I have with the 'never traverse' list, as i understand it, is that this wouldn't solve the recurring problem that keeps coming up with valid html (note: valid html doesn't mean it's necessarily 'good') :

<a href=>
  <article>
     <h2>foo</h2>
      other stuff goes here
  </article>
</a>

in that example the link has no name because the article has no name, even though the article has content that could be used as the name. It seems to me that for scenarios like this, if an element (which honestly might be limited to hyperlinks - this is where the problem keeps coming up) runs into an element like article and gets no name from it, it shouldn't stop there and not traverse into the article to get something useful.

However, if the article was given a name, then i think it's fair for the name computation to stop there and not grab all the content of the article to create it's accessible name.

cc @jcsteh for your thoughts and to correct me if i misunderstood what you were suggesting

jcsteh commented 10 months ago

in that example the link has no name because the article has no name, even though the article has content that could be used as the name. It seems to me that for scenarios like this, if an element (which honestly might be limited to hyperlinks - this is where the problem keeps coming up) runs into an element like article and gets no name from it, it shouldn't stop there and not traverse into the article to get something useful.

So on one hand, I grant that it's really not ideal to have no name for something that occurs in the wild. On the other hand, you could probably find examples of all sorts of inaccessible behaviour in the wild that are considered "valid HTML". In addition, we do not want to encourage this behaviour. Maybe this is a case where we need to make article-in-link part of the repair behaviour, but not considered "valid" from an a11y perspective. That is, I absolutely want a11y validators to flag this as bad behaviour.

When you see this pattern, how big is the "article"? I don't like the idea of an entire article (in the true sense of the word) being shoved into the label of a link.

However, if the article was given a name, then i think it's fair for the name computation to stop there and not grab all the content of the article to create it's accessible name.

That already happens for everything in the "never traverse" list.

accdc commented 10 months ago

Unfortunately the problem with links is more broad, in that according to the HTML spec you can put anything within one. E.G a layout table, landmark region, list markup, and much else, all of which breaks this computation. Devs keep doing this because according to HTML there is nothing wrong with it according to validators.

As a possible workaround, would it be of benefit if we added an exception for link, where, if a name is not returned using the regular algorithm, it will attempt to traverse all children regardless of role to return any contained textual content as a failsafe?

jcsteh commented 10 months ago

IMO, we still shouldn't consider this valid. It's a bad pattern, valid HTML or not, and it hurts users; it's hard to say whether an unnamed link or a link with a 500000 character label is worse. We can call it repair, fine, but IMO, it should still be flagged as invalid by a11y validators/checkers. But I'm one voice and I understand there is complexity here. I'm not going to push any further if the consensus is to make a "valid" exception for this case, as much as I might cringe about it. :)

JAWS-test commented 10 months ago

@jcsteh: There may also be elements in the link that provide a very short AccName and should be valid, and yet do not work, i.e. links with blockquote or figure have no AccName. I think that "never traverse" should not occur for any element or role. I have nothing against setting a maximum AccName length and then simply truncating the text afterwards

jcsteh commented 10 months ago

At that point, we're just always traversing everything. Are you suggesting that it's reasonable to traverse a listbox if it happens to be a descendant of a name from: contents target? Consider:

data:text/html,<div role="table"><div role="row"><div role="cell"><select size="2"><option>a</option><option>b</option><option>... several thousand more options ...</option></select></div></div></div>

I don't think it's at all reasonable for the cell to have the name "a b ... several thousand more options ..."

JAWS-test commented 10 months ago

@jcsteh No, because of AccName 2 E:

If the embedded control has role combobox or listbox, return the text alternative of the chosen option.

jcsteh commented 10 months ago

Fair enough; I forgot listbox was covered by that rule. What if it were role="tree" with 10000 treeitems?

accdc commented 10 months ago

In the case of role=tree the same would be true, only the selected treeitem is valid as the returned option for the widget.

All of these are moot points though, since a selectable listbox or a tree should not be embedded in a link. We shouldn't be endorsing embedded widgets within active elements.

If we do allow full traversal within a link, it should only apply to static container elements that are not interactive widgets with selectable options.

MarioBatusic commented 9 months ago

@scottaohara I tested you example (extended and with or without aria-label on the link with Chrome and JAWS / NVDA. The more dangerous situation is, when a dev puts an aria-label on such a link: in this case he/she hides all the content of the article from screen reader user. Without an aria-label it would be quite OK, if screen readers would optimize their link handling - e.g. only saying link start on the beginning and link end on the end of the whole contents. At the time NVDA shows in Braille and reads the content well with all the set structure. JAWS puts all paragraphs of text in Braille on one line - I find it ungly.

scottaohara commented 9 months ago

yes, screen readers can handle this differently though, and do seem to be working around that the hyperlink has no name, but they are exposing something anyway. Which... unfortunately, is not something I believe we can consistently bank on always working.

cookiecrook commented 9 months ago

For future Deep Dive planning, please include @twilco and me.