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 for="fruit">Favourite fruit</label> <select id="fruit"> <option>Choose an option</option> <option>Apple</option> <option>Apricot</option> <option>Avocado</option> </select>
<option> elements each have a specific semantic meaning that is understood by accessibility APIs.
<select> elements are given matching
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>
<select> element is defined with a native
hasPopup property, with a value of
haspopup property indicates the element can trigger a popup. The
menu value specifies that the popup will be a menu.
1.4: Current state
<select> element will have a state of
expanded: false until the user expands it.
1.5: Current value
<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
- SPACE - View all options in a dropdown
- ↓ and ↑ - Navigate through dropdown options
- ENTER or SPACE - Select a dropdown option
2. A non-native example
The ChatGPT example is a non-native component as it uses the
<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.
<button> element will be defined in the accessibility tree as
button, which could confuse some assistive technology users.
<li> elements have roles of
listitem, but these are not intended for dynamically displayed dropdown information.
This component will have an accessible name of “Choose your favourite fruit” in the accessibility tree, which is acceptable.
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.
|Native solution||Non-native solution|
|Current value||Available||Not available|
|Keyboard accessible||Available||Not available|
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.