iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔽

WCAG Uncertainty Points #4: Why is a 'select' element blocked while a custom dropdown is allowed, despite looking the same? Evaluating WCAG 3.2.2 (On Input)

に公開
<!-- Pattern A: select -->
<select onchange="filter()">
  <option>All</option>
  <option>Electronics</option>
  <option>Food</option>
</select>

<!-- Pattern B: Custom Dropdown -->
<div class="custom-select">
  <button type="button">Select Category</button>
  <ul>
    <li><button type="button">All</button></li>
    <li><button type="button">Electronics</button></li>
    <li><button type="button">Food</button></li>
  </ul>
</div>

For common filtering UI, even when the visual appearance is almost identical, Pattern A might be judged as a violation of 3.2.2, while Pattern B is considered OK. Why does this difference arise?

3.2.2 focuses on "Change of Settings"

Success Criterion 3.2.2 On Input (Level A) prohibits "a change of context without warning when a user changes the setting of a UI component."

The key point here is that the W3C clearly distinguishes between "change of setting" and "activation."

Changing the setting of a checkbox, entering text in a text field, or selecting an option in a list control changes the setting of a component, but that is not the case when operating a link or button.

Note: This Success Criterion covers changes of context resulting from changing the setting of a control. Clicking a link or a tab in a tab control activates the control without changing its settings.
Source: Understanding Success Criterion 3.2.2: On Input

In other words, checking a checkbox, entering text, or changing a selection in a select element are classified as "change of setting," whereas clicking a link or button is classified as "activation" of the control.

Operation Classification Subject to 3.2.2
Changing option in select Change of setting ✅ Yes
Clicking a button Activation ❌ No

Furthermore, 3.2.2 prohibits "change of setting" from causing a change of context [1]. [2]

Why Pattern A might be NG

An implementation where immediate filtering occurs via onchange on a select element is structured such that the process is triggered by a "change of setting." If this process triggers a change of context, such as a full page reload or opening a new window, it is a violation of 3.2.2.

On the other hand, updates such as replacing a result list within the same view are not major page changes but rather "content changes," and thus do not constitute a 3.2.2 violation.

If the process causes a change of context, use a configuration with an explicit execution button instead of immediate execution upon selection.

<!-- NG -->
<select onchange="filter()">...</select>

<!-- OK: Use an execution button -->
<select>...</select>
<button onclick="filter()">Filter</button>

Why Pattern B is OK

In a custom dropdown where items equivalent to options are button elements, filtering is executed by the user's explicit action of pressing a button. Since this is a change caused by activation, it is outside the scope of 3.2.2.

Even if the behavior of "being filtered the moment you choose" is the same, the judgment changes because the classification of the operation is different.

Adding ARIA or roles changes the situation

Caution is needed when adding ARIA for accessibility purposes.

Minimum configuration to comply with 4.1.2

To make Pattern B compliant with 4.1.2 (Name, Role, Value), only aria-expanded is necessary.

<div class="custom-select">
  <button type="button" aria-expanded="false">
    Select Category
  </button>
  <ul hidden>
    <li><button type="button">All</button></li>
    <li><button type="button">Electronics</button></li>
    <li><button type="button">Food</button></li>
  </ul>
</div>
  • role="menu" and aria-haspopup are unnecessary. If applied to a collapsible dropdown that is not implemented as a menu pattern (e.g., application menus), screen readers will expect menu-specific interactions (such as arrow key navigation). If the implementation cannot satisfy those expectations, the interaction will fail.
  • aria-expanded is required. It is essential for communicating the open/closed state to assistive technology.
  • aria-controls is nice to have. It is not mandatory, but it is a recommended attribute.

Pattern using role="listbox" + role="option"

To communicate a custom dropdown correctly to screen readers, there is a pattern that uses role="listbox" + role="option".

<div class="custom-select">
  <button type="button" aria-expanded="false" aria-haspopup="listbox">
    Select Category
  </button>
  <ul role="listbox">
    <li role="option">All</li>
    <li role="option">Electronics</li>
    <li role="option">Food</li>
  </ul>
</div>

role="option" is treated as an option within a listbox and is semantically equivalent to an option in a select. In other words, since it is communicated to assistive technology as a "selection of an option" rather than a "button click," it can be treated similarly to the "change of setting" defined by the W3C.

If this implementation triggers immediate filtering after selection, there is room for it to be pointed out as a 3.2.2 issue.

Implementation 3.2.2 Judgment
button element only (no role) ✅ OK (Activation)
With role="option", immediate execution ⚠️ Potential issue
With role="option", execution button exists ✅ OK

Putting a button inside role="option" does not make it OK

You might think, "Wouldn't it be OK if I put a button inside a role="option" to trigger activation?" but this does not function as intended.

<!-- This is NG -->
<li role="option">
  <button>Electronics</button>
</li>

With role="option", the roles of child elements are treated as not being conveyed to assistive technology. Therefore, the inner button is not recognized as a button, and the behavior assumes it will only be read as text.

Even if you try to avoid 3.2.2 by adding a button inside an element intended to be accessible via role="option", this implementation is fundamentally broken according to ARIA specifications.

Summary

3.2.2 distinguishes between "change of setting" and "activation" and prohibits changes of setting from causing a change of context. Even if they appear to be the same "filtering UI," the starting point for judgment changes depending on whether it is a select or a button, and the presence of a violation is determined by whether it is accompanied by a change of context. The perspective of 3.2.2 is to judge based on element semantics and operation classification, not appearance.

Furthermore, ARIA is a powerful mechanism that redefines the role of an element, rather than just an attribute to "reinforce accessibility." If you rewrite the role, the success criteria tied to it also change. ARIA added with the intention of improving accessibility may, in some cases, create new burdens for compliance.

When native HTML element semantics are sufficient, using them directly is the most robust approach. Before considering ARIA, stopping to confirm "Can this not be expressed with native elements?" is a fundamental practice for all accessibility implementation, not just for 3.2.2.


Note: This article includes independent interpretations while deciphering the WCAG guidelines.

脚注
  1. This refers to major changes such as opening a new window, navigating to a different page, significant shifts in focus, or major rearrangement of page structure. Updates like refreshing a list within the same view are called "content changes" and are not included in changes of context. (WCAG 2.2 Glossary) ↩︎

  2. Related failure example: F37: Failure of Success Criterion 3.2.2 due to opening a new window without warning when the selection of a radio button, check box or select list is changed. ↩︎

Discussion