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

Spec for menu/menuitem does not provide enough author guidance for structure #2300

Open
aleventhal opened this issue Aug 1, 2024 · 12 comments
Labels

Comments

@aleventhal
Copy link
Contributor

Describe your concern

UAs/ATs need a way to determine the parent menuitem that caused a menu to be opened. This is important for Talback on Android, which provides the name of the menu along with the choices.

While the spec does not provide this, somehow the ARIA Authoring Practices Guide does, which says:
"If activating a menuitem opens a submenu, the menuitem is known as a parent menuitem. A submenu's menu element is:

  • Contained inside the same menu element as its parent menuitem.
  • Is the sibling element immediately following its parent menuitem."

If this is generally followed, it would allow the UA to determine which menuitem opened a given submenu, by navigating from the role="menu" object to the previous sibling in the accessible object tree.

One caveat is that this is also slightly incomplete, because it says "sibling element" instead of "sibling accessible object". Authors should also be able to use aria-owns to create these relationships. The important thing is the final tree structure, not the DOM structure.

Link to the version of the specification or documentation you were looking at at.

https://w3c.github.io/aria/#menuitem
https://w3c.github.io/aria/#menu
https://www.w3.org/WAI/ARIA/apg/patterns/menubar/

Link to documentation:

Does the issue exists in the editors draft (the editors draft is the most recent draft of the specification)?

Yes

@jnurthen jnurthen added the Agenda label Aug 1, 2024
@scottaohara
Copy link
Member

resolving this would probably help close out #1711 and #1120 ?

@aardrian
Copy link
Contributor

Can you provide an example of a parent/child and sibling/child relationship structure? I think it might be useful for better understanding the issue (at least it would be for me).

@cookiecrook
Copy link
Contributor

Discussion of edge cases and usage patterns in the WG meeting today was complex enough that perhaps we should consider defining a nesting algorithm for menus/submenus, trees, treegrids, etc. taking into account DOM tree, aria-owns, aria-controls, the image map edge case, generic in-betweener elements, and more...

@spectranaut
Copy link
Contributor

Discussed in today's meeting: https://www.w3.org/2024/08/22-aria-minutes#t09

However, the exact next steps were not clear and we ran out of time. To pick up next meeting.

@accdc
Copy link
Contributor

accdc commented Aug 22, 2024

Regarding menus and trees, all of these should be supported equally.

<ul role="menu" style="list-style: none;" >
<li>
<a role="menuitem" aria-haspopup="true" tabindex="0" aria-expanded="true" > Branch </a>
<ul role="menu" style="list-style: none;" >
<li>
<a role="menuitem" tabindex="-1" > Leaf </a>
</li>
</ul>
</li>
</ul>
<ul role="menu" style="list-style: none;" >
<li role="menuitem" aria-haspopup="true" tabindex="0" aria-expanded="true" >
<a> Branch </a>
<ul role="menu" style="list-style: none;" >
<li role="menuitem" tabindex="-1" >
<a> Leaf </a>
</li>
</ul>
</li>
</ul>
<ul role="tree" aria-label="Name" style="list-style: none;" >
<li>
<a role="treeitem" tabindex="0" aria-expanded="true" > Branch </a>
<ul role="group" style="list-style: none;" >
<li>
<a role="treeitem" tabindex="-1" > Leaf </a>
</li>
</ul>
</li>
</ul>
<ul role="tree" aria-label="Name" style="list-style: none;" >
<li role="treeitem" tabindex="0" aria-expanded="true">
<a> Branch </a>
<ul role="group" style="list-style: none;" >
<li role="treeitem" tabindex="-1" >
<a> Leaf </a>
</li>
</ul>
</li>
</ul>

Ideally this wouldn't be dependent on list markup, but rather the role nesting structure.

@aleventhal
Copy link
Contributor Author

How about the following algorithm for finding the name of menu from a menuitem:

Starting from menuitem:

  1. Navigate to ancestor menu (up to 3 ancestors up). If none found, return the empty string.
  2. If another element controls this menu, navigate to that. If a menuitem, skip to step 4, else return the empty string.
  3. Navigate to previous sibling menu item (up to 3 previous siblings). If none found, return the empty string.
  4. If expanded, use accessible name of this menu item, else return the empty string.

@accdc
Copy link
Contributor

accdc commented Aug 23, 2024

It does work in these scenarios, but it's very easy to break by changing the nested elements. E.G. By adding a div around the elements within the LI.

I don't know what architecture you are running, but do you have to use parent/child/sibling nodes, or can you also use a CSS selector?

E.G.

From menuitem, move to the containing role=menu,
check for a parent role of role="menuitem" then process.
Otherwise, from role="menu", use parentNode.querySelector('[role="menuitem"]') to get the first menuitem, check if this is the same as the originating menuitem node and ignore if so, otherwise return it's name.

Unfortunately parentNode assumes that both menuitem and menu are siblings in the same direct container, but the search would be more flexible by moving up based on role, then querying again by role.

Like I said though, this may be naive since I don't know if this is possible in your code.

@aleventhal
Copy link
Contributor Author

Updated proposal:

  1. Navigate to ancestor object with role menu. If none found, return the empty string.
  2. If the menu has an accessible name, return that
  3. If another element has a controls relationship pointing this menu, inspect that. If that object has a name, return that.
  4. Inspect the previous sibling menu item. If none found, return the empty string.
  5. If expanded, use accessible name of this menu item, else return the empty string.

@smhigley
Copy link
Contributor

Adding some examples of current menu implementations that would return the wrong accName if using the ancestor menu > sibling menu item approach, based on what was discussed in the call.

I used examples from the open ui menu page, plus the current version of Fluent just because I work on it :D. I left out libraries where the examples didn't include popup menus.

  • Fluent (legacy): there are multiple intervening generics, but each menu is rendered in its own layer at the end of the DOM. The closest ancestor > sibling menu item of a second-level menu would be the last menuitem of the higher-level menu.
  • Fluent (current): same approach with layers, with the same issue.
  • Ant design: same approach as Fluent, each menu is rendered in its own layer/div at the end of the DOM, so the closest ancestor > sibling of a second-level menu would be the last menu item of the parent menu.
  • Bootstrap: doesn’t use menu roles
  • Evergreen: doesn’t have multi-level menus, but the single-level menus are rendered in their own layer at the end of the DOM. Seems likely mutli-level menus would have the same issue.
  • Lightning design: this one would work! It renders flyout menus inline in the DOM.
  • Semantic UI: renders flyouts inline in the DOM, but does not use menu roles.
  • Spectrum: renders each submenu in its own layer at the end of the DOM, so the ancestor > sibling check would always be the last menuitem of the parent menu.

TL:DR: 1 example would return the right name, 5 would not, and 2 are N/A because of the lack of menu semantics.

@spectranaut
Copy link
Contributor

Notes from yesterday's WG meeting: https://www.w3.org/2024/08/29-aria-minutes#t04

@spectranaut
Copy link
Contributor

Discussed again in today's meeting: https://www.w3.org/2024/09/05-aria-minutes#t05

Decision was to encourage use of aria-controls or aria-owns in menu or menuitem spec, after some more experimentation between the two options.

@aleventhal
Copy link
Contributor Author

aleventhal commented Sep 12, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants