nested-interactive-controls-absent

Interactive controls must not be nested

automaticWCAG 4.1.2 (A)confidence: highseverity: seriousrobust

Checks that an interactive control (link, button, form control, or ARIA widget role) does not contain another interactive control.

Applies to. Applies to elements matching the interactive-control set (native a[href], button, input (not hidden), select, textarea; or an explicit ARIA widget role: button, link, checkbox, radio, switch, tab, textbox, combobox, listbox, menuitem, menuitemcheckbox, menuitemradio, option, slider, spinbutton, searchbox, treeitem, taken from the role attribute's first token naming a known role, matched in any case, so role="foo button" and role="BUTTON" count and role="none button" does not). The container is applicable regardless of whether it is itself focusable, focusability is only used to decide whether a descendant nests an interactive control. A custom element whose ElementInternals give it one of those widget roles (this.attachInternals().role), with no role attribute naming another, counts as a native element with that role does.

Expectation. The element does not contain, as a descendant, another operable interactive control (e.g. a <button> wrapping a <select>, or a link containing a checkbox). Nested interactive controls are not reliably announced or operable via assistive technology, activating the outer control and the inner one become ambiguous, and some AT only exposes one of the two. It FAILS where the outer control's role has presentational children (WAI-ARIA 1.2: button, checkbox, img, menuitemcheckbox, menuitemradio, meter, option, progressbar, radio, scrollbar, separator, slider, switch, tab): the nested control's role is removed there, so a control that still takes focus has no role to determine (SC 4.1.2, as ACT 307n5z has it). Elsewhere (a link, textbox, combobox, listbox, menuitem, searchbox, spinbutton or treeitem holding a control) both keep their roles; HTML's content model forbids the nesting, but whether a name, role or value is lost depends on the page, so it is reported at CANTTELL.

What a finding reports.

  • nestedElements: the controls nested inside this one, one tag name per control.

These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.

Examples

pass
<button>Click me</button>

No nested interactive control.

fail
<button>
<input type="checkbox"> Accept
</button>

A <button> contains a nested <input type="checkbox">, another focusable control. A button's children are presentational, so the checkbox loses its role.

How to check

When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.

NESTED_INTERACTIVE_CONTROL

  1. Inspect the control the report names, such as a link, and note the control nested inside it.
  2. With the keyboard, tab to each of the two and activate it, and check that each does its own job and that activating the inner one does not also activate the outer one.
  3. With a screen reader, move to each and note whether both are announced with their own role and name, or whether the outer control's name swallows the inner one's.

Pass. Both controls can be reached and operated on their own, each with its own role and name. Still move the inner control out: HTML does not allow it there.

Fail. The inner control cannot be reached or operated on its own, or it is not announced with its own role and name, so its name, role or operation is not available (WCAG 4.1.2, 2.1.1).

View source