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

New rule: flag nested interactive roles #601

Closed
marcysutton opened this issue Nov 3, 2017 · 23 comments
Closed

New rule: flag nested interactive roles #601

marcysutton opened this issue Nov 3, 2017 · 23 comments
Assignees
Labels
best practice rules Issue or false result from an axe-core rule
Milestone

Comments

@marcysutton
Copy link
Contributor

marcysutton commented Nov 3, 2017

Nested Interactive Roles

Ensures interactive controls are not nested so they work with keyboards and screen readers

Tags: best-practice, experimental, cat.keyboard

Help text

Ensure interactive controls are not nested.

Selector

a, button, [role="link"], [role="button"]

More info

The code sample below shows a few places where aXe should have flagged nested interactive roles. The developer in this case added a role of link with tabindex, nested a link with role=button and tabindex inside of it, and then put an input with type of image inside. That's a triple-whammy for nested controls, that we should be able to catch pretty easily since it doesn't even use click events.

<div role="link" tabindex="40" title="link label">
  <a href="/url-went-here" title="link label" role="button" tabindex="12">
    Link label
    <div class="link-icon">
        <input type="image" alt="add to stop throwing error">
    </div>
  </a>
</div>

Checks

  • none: nested-interactives
@marcysutton marcysutton added the rules Issue or false result from an axe-core rule label Nov 3, 2017
@WilcoFiers
Copy link
Contributor

Can you write this in the rules format?
https://github.com/dequelabs/axe-core/blob/develop/doc/rule-proposal.md

@marcysutton
Copy link
Contributor Author

marcysutton commented Mar 24, 2018

Here's a Codepen showing what happens with nested links. The browser automatically reformats nested anchor tags, but not when ARIA role="link" is wrapping an anchor tag. https://codepen.io/marcysutton/pen/YaxJVm?editors=1010#

Alternative rule name: dont-do-it 😂

There's some language in the ARIA 1.1 specification around presentational children, which helps to clarify some of this. Browser behavior varies, even though the spec says you can't nest these roles: Chrome and Voiceover announce links nested inside of the button role, but Safari does not. I still have to test Windows but this rule makes a lot of sense to add. https://www.w3.org/TR/wai-aria-1.1/#childrenArePresentational

@dylanb
Copy link
Contributor

dylanb commented Apr 1, 2018

What is the accessibility impact of this? The keyboard focus can move from the one element to the next. The user can interact with both.

If there is no real impact, then this should be a best practice.

@marcysutton
Copy link
Contributor Author

marcysutton commented Apr 4, 2018

The accessibility impact is that it's extremely confusing to screen reader users to hear "Link label button link" or an empty "button" because of misused HTML and ARIA. Mixing of interface controls makes them very hard to understand from a screen reader user's perspective, even if they are coincidentally still operable.

As one example, when the browser changes your markup from <button><button>Text</button</button> to <button></button><button>Text</button>, you end up with an unlabeled button. We could nip these problems in the bud with a dedicated rule.

@dylanb
Copy link
Contributor

dylanb commented Apr 5, 2018

The unlabelled button problem will be found by the button-name rule (the beauty of testing the DOM)

Potential confusion problems as well as event handler propagation problems are not candidates for zero-false-positive detection

Seems like a candidate for needs review at most

@marcysutton
Copy link
Contributor Author

How could nonconforming HTML ever be okay, especially when it presents a horrible screen reader experience? This seems like an obvious win to me. Can you provide more concrete examples of potential false positives?

@dylanb
Copy link
Contributor

dylanb commented Apr 12, 2018

<h2>Very broken</h2>
<p>This group of inputs is very broken because only the fake link can be triggered</p>
<div role="link" tabindex="0" title="link label" onclick="event.stopPropagation();event.preventDefault();alert('clicked fake link')">
  Link Text
  <a href="/url-went-here" title="link label" role="button">
    Role=["button"] Text
    <div class="link-icon">
        <input type="image" alt="add to stop throwing error" onclick="event.stopPropagation();event.preventDefault();alert('clicked input')">
    </div>
  </a>
</div>
<H2>Not as broken</h2>
<p>This group of inputs is less broken because both the link and the button can be triggered</p>
<a href="/url-went-here" title="link label" role="button">
    Role=["button"] Text
  <div class="link-icon">
    <input type="image" alt="add to stop throwing error" onclick="event.stopPropagation();event.preventDefault();alert('clicked input')" />
  </div>
</a>

https://codepen.io/holistica11y/pen/EEzVYy

@stevefaulkner
Copy link

stevefaulkner commented Apr 13, 2018

Nesting which results in duplication of announcements in the aural UI is a problem. For sure there are some examples where the result is sort of OK in some browsers with some AT (unknown unless fully tested), but the cost of teasing those out is worth less than accepting that the non-conforming (in HTML) nesting of interactive elements is an issue that should be flagged as an error.

Here is a neat example of a label/interactive control nesting that causes multiple issues; incorrect name calc and misdirected mouse click. This doesn't appear to be caught by aXE (version 3.3)

Also interactive nesting conformance errors for marcy's test page. When the DOM is tested (instead of the HTML code) the errors are reduced from 9 to 7.

@aardrian
Copy link

Is the argument that this is not a 4.1.1 failure after the browser fixes otherwise invalid code to generate the DOM?

@scottaohara
Copy link
Contributor

Please consider the following:

<button>normal button</button>

<div role="button" tabindex="0">
  Parent button
  <div role="button" tabindex="0">
    Nested button
  </div>
  <p>Just some more content here.</p>
</div>

view example at - https://codepen.io/scottohara/pen/RMmyZX

aXe reports no errors when testing this example.

A quick test with Safari + VO (mac High Sierra), the parent button can be focused with Tab key or VO + left/right arrows, and all the content within will be announced.

VO focus (using VO + left/right arrows, with quick nav on or off) can not move into the nested button to interact with the nested button specifically.

Tab key will move focus to the nested button, but VO focus remains on the parent button. The focused nested button is not individually announced.

I have further (much more complex) examples of nested interactive elements causing similar / other significant issues when using JAWS and NVDA. But to keep myself from writing a blog post worth of examples here, I'll save those for if you'd like further evidence.

Hope this was helpful.

@stevefaulkner
Copy link

Suggest that a practical approach would be to implement the conformance rules as is and loosen them as cases that are clearly not problematic are identified.

@dylanb
Copy link
Contributor

dylanb commented Apr 13, 2018

This proposed rule is defined way too broadly. For example this is perfectly fine markup:

<ul role="menubar">
    <li aria-haspopup="true" role="menuitem" tabindex="0">
        One
        <ul role="menu">
            <li role="menuitem" tabindex="0">
                  One One
            </li>
...

menuitem is defined as a widget role and having menuitems as children (ultimately) of other menuitems is actually encouraged.

So IMO, we should close this issue and open specific issues for specific nestings that we know do not work

EDIT: doing this would allow us to also give specific guidance as to why this is a problem

@stevefaulkner
Copy link

stevefaulkner commented Apr 13, 2018

There are a sets of rules for how interactive elements/roles can be nested defined in the HTML/ARIA/ and ARIA in HTML specs. Suggest that these are a good place to start.

@dylanb btw your code example is conforming ;-)

@marcysutton
Copy link
Contributor Author

So IMO, we should close this issue and open specific issues for specific nestings that we know do not work

I was planning to start with button and link roles. How about we edit the issue instead of chucking it out and starting over?

@marcysutton
Copy link
Contributor Author

I just found some code related to this in the wild:

<a class="no-underline" href="/lessons/new" aria-label="Add a new lesson">
<button class="
      flex items-center justify-center
      f6 fw6 ttu b
      ba br-100
      pointer
      b--green
      bg-green white
    ">
<svg fill="currentColor" preserveAspectRatio="xMidYMid meet" height="1em" width="1em" viewBox="0 0 20 20" class="f3 gray" style="vertical-align: middle;"><g><g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd"><g id="ico_plus"><path d="M11.5,9.5 L11.5,3 L9.5,3 L9.5,9.5 L3,9.5 L3,11.5 L9.5,11.5 L9.5,18 L11.5,18 L11.5,11.5 L18,11.5 L18,9.5 L11.5,9.5 Z"></path></g></g></g></svg>
</button>
</a>

The button inside of the link causes an extra tab stop and quite a bit of keyboard confusion. After reading through ARIA in HTML that Steve so awesomely provided, I see that interactive content is not allowed for buttons nor links:

Allowed ARIA roles, states and properties

Role Description Required Properties Supported Properties Kind of content - (new) Descendant restrictions - (new)
button An input that allows for user-triggered actions when clicked or pressed. See related link. none aria-expanded (state)aria-pressed (state) Interactive content Flow content, but there must be no interactive content descendant.
link An interactive reference to an internal or external resource that, when activated, causes the user agent to navigate to that resource. See related button. none aria-expanded (state) Flow content Flow content, but with no interactive content or a element descendants.

axe-core should catch these and some of the ARIA cases, IMO. I can put together a list when I get back from leave unless someone beats me to it.

@dylanb
Copy link
Contributor

dylanb commented Aug 28, 2018

@marcysutton If you are doing <button>, you should add <input> to the list - especially type="button", type="submit" and type="image" but possibly more...

@schne324
Copy link
Member

schne324 commented Sep 7, 2018

Came across this type of setup and wondering if this too would fall into this rule

<ul role="menubar">
  	<li role="presentation">
		<a tabindex="-1" href="https://foo.biz">
			<span role="menuitem" tabindex="0">Foo</span>
		</a>
	</li>
</ul>

Tested with VO and I was able to traverse to both the link and menuitem. I realize that this is slightly different than most of the above examples because they've taken the anchor out of the natural tab order but it still creates a potential confusing experience for screen reader users.

Feel free to ping me for the public url I found this on

@dylanb
Copy link
Contributor

dylanb commented Sep 24, 2018

@schne324 your case is a hybrid of my case and something else. That structure is completely broken from an ARIA markup perspective. All interim structures should be marked role="presentation" and the anchor with the role="menuitem" inside it is just plain wrong.

A rule that flags that type of bad structure is different from what is being proposed here but would be very valuable.

@straker straker added this to the Axe-core 3.4 milestone Jun 3, 2019
@WilcoFiers WilcoFiers modified the milestones: Axe-core 3.4, axe-core 3.5 Aug 22, 2019
@straker straker removed this from the Axe-core 3.5 milestone Nov 6, 2019
@straker straker added this to the Axe-core 4.0 milestone Jan 15, 2020
@WilcoFiers WilcoFiers removed this from the Axe-core 4.0 milestone Apr 28, 2020
@gaurav5430
Copy link

For a more general understanding of this, are there any guidelines on whether or not to have any nested interactions at all or not in ARIA widgets? Are there any example ARIA widgets with nested interactions. Can combobox be considered one such example?

As a specific example, if someone replaces button inside button or role="button" inside role="button" with div inside div and handles the keyboard navigation, would it be considered correct behaviour?

So, replacing

<button>
  <button>some</button>
  thing
</button>

with

<div onclick="fun1" onkeypress="fun2">
  <div onclick="fun3" onkeypress="fun4">
    some
  </div>
  thing
</div>

If yes, why isn't it allowed in ARIA or HTML by default? I mean why restrict this usage for buttons and links but allow for divs?

@stevefaulkner
Copy link

stevefaulkner commented Jun 6, 2020

@gaurav5430
I suggest your replacement example would fall foul of WCAG 2.1 SC 4.1.2 amongst other SC, as they are interactive UI elements that do not convey a role, if you add an appropriate role to each then you may fall foul of the nesting rules.

are there any guidelines on whether or not to have any nested interactions at all or not in ARIA widgets?

The authoring requirements for use of ARIA in HTML define what roles can be nested within each role.

@gaurav5430
Copy link

I suggest your replacement example would fall foul of WCAG 2.1 SC 4.1.2 amongst other SC, as they are interactive UI elements that do not convey a role

Thanks, i understand, any interactive elements should convey a role, either by HTML semantics or by explicitly assigning aria roles, which means in this case, div inside div would be invalid, unless they are assigned aria roles.

if you add an appropriate role to each then you may fall foul of the nesting rules.

But this also means that nested interactive elements are allowed, if we can find out the correct aria role hierarchy for them. Hence the question, if nested interactions are allowed in any case, why are they not allowed for button inside button or generally in HTML, is this only because when using nested aria roles as defined in ARIA in HTML the screenreader can parse/read it better as compared to the case of button inside button or button inside link? Or are there other usability issues in general with nested interactions, that they haven't been allowed ?

@stevefaulkner
Copy link

stevefaulkner commented Jun 7, 2020

@gaurav5430 wrote:

Hence the question, if nested interactions are allowed in any case, why are they not allowed for button inside button or generally in HTML

The constraints on nested interactive roles are based on the constraints already defined in HTML, for any roles that match built in HTML elements.

not conforming in HTML

<button>
<button>
</button>
</button> 

not conforming in HTML

<div role=button tabindex=0>
<div role=button tabindex=0>
</div>
</div>

@padmavemulapati
Copy link

Verified with the latest develop branch code base of axe-core (dated28/12/2020):
New rule: nested-interactive is working as expected:
fail condition test snippet:
<button id="fail1"><span tabindex="0">fail</span></button>
<div role="button" id="fail2"><input /></div>
<div role="tab" id="fail3"><button id="pass6">fail</button></div>
<div role="checkbox" id="fail4"><a href="foo.html">fail</a></div>
<div role="radio" id="fail5"><span tabindex="0">fail</span></div>

image

and pass condition test snippet:
<button id="pass1">pass</button>
<div role="button" id="pass2">pass</div>
<div role="tab" id="pass3">pass</div>
<div role="checkbox" id="pass4">pass</div>
<div role="radio" id="pass5"><span>pass</span></div>

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
best practice rules Issue or false result from an axe-core rule
Projects
None yet
Development

No branches or pull requests

10 participants