maxdesign.com.au

Published:

I recently asked ChatGPT to: "write an accessible dropdown menu".

The response was:

<div class="dropdown">
  <button
    class="dropdown-button"
    aria-expanded="false"
    aria-haspopup="true"
  >
    Menu
  </button>
  <ul class="dropdown-list" aria-hidden="true">
    <li><a href="#">Option 1</a></li>
    <li><a href="#">Option 2</a></li>
    <li><a href="#">Option 3</a></li>
    </ul>
</div>

At first glance, this is a surprisingly good component as it uses some core ARIA attributes correctly.

But is this the right approach?

Ideally, we should always try to use native elements to build components rather than resorting to ARIA. As per the W3C's "Using ARIA":

If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

A non-native solution means that the component has been built using elements differently than the intended purpose - in this case, a button is used to create a dropdown menu.

Let's look at a native vs non-native example and compare their accessibility.

1. A native example

A native dropdown solution would involve using the <label>, <select> and <option> elements.

<label for="fruit">Favourite fruit</label>
<select id="fruit">
  <option>Choose an option</option>
  <option>Apple</option>
  <option>Apricot</option>
  <option>Avocado</option>
</select>

1.1: Role

The <label>, <select> and <option> elements each have a specific semantic meaning that is understood by accessibility APIs.

1.2: Name

If the <label> and <select> elements are given matching for and id values, the <select> will then have an accessible name.

<label for="fruit">Favourite fruit</label>
<select id="fruit">
  <option>Choose an option</option>
  <option>Apple</option>
  <option>Apricot</option>
</select>

1.3: Properties

The <select> element is defined with a native hasPopup property, with a value of menu.

The haspopup property indicates the element can trigger a popup. The menu value specifies that the popup will be a menu.

1.4: Current state

The <select> element will have a state of expanded: false until the user expands it.

1.5: Current value

If an <option> is selected, it will be defined in the accessibility tree as the value - i.e. “Apple”.

1.6: Keyboard accessible

A range of pre-defined keystrokes can be used to interact with the <select> and <option> elements.

2. A non-native example

The ChatGPT example is a non-native component as it uses the <button>, <ul> and <li> elements to create a dropdown - like the Bootstrap button.

<button>Choose your favourite fruit</button>
<ul>
  <li>Apple</li>
  <li>Apricot</li>
  <li>Avocado</li>
</ul>

Let's look at this component before any accessibility is manually added.

2.1: Role

The <button> element will be defined in the accessibility tree as button, which could confuse some assistive technology users.

The <ul> and <li> elements have roles of list and listitem, but these are not intended for dynamically displayed dropdown information.

2.2: Name

This component will have an accessible name of “Choose your favourite fruit” in the accessibility tree, which is acceptable.

2.3: Properties

There are no additional properties assigned to the elements to provide additional context.

2.4: Current state

There is no native way to inform users about the dropdown’s current state.

2.5: Current value

There is no native way to inform users of the currently selected value. If a screen reader user returns to the component later, there is no indication that an option has been selected.

2.6: Keyboard accessible

The component will have no native keystrokes defined. So, it is not keyboard accessible.

Scorecard?

Native solution Non-native solution
Name Available Available
Role Combobox Button (Incorrect)
Properties hasPopup: menu Not available
Current state Expanded: false/true Not available
Current value Available Not available
Keyboard accessible Available Not available

All of these problems can be fixed using a combination of ARIA and JavaScript. But it takes a lot of additional work.

Here are some resources that can help:

Bottom line: the ChatGPT solution could be made to work, but using a native solution where possible would be far more efficient and effective.