DEV Community

Cover image for Creating A Custom, Accessible Drop Down

Creating A Custom, Accessible Drop Down

Emma Bostian ✨ on February 28, 2019

Note: I'm still learning accessibility, so if you find a flaw in my method, please let me know in the comments below! Creating custom components i...
Collapse
 
lexlohr profile image
Alex Lohr

A small addition: it could also help to select the correct WAI ARIA roles for the elements, i.e.

<ul role="listbox">
  <li role="option">
  ...
</ul>
Enter fullscreen mode Exit fullscreen mode

Another minor thing: I prefer to use global event handlers (document.addEventListener(...)) and filter the event target after their attributes in the handler if I deal with plain vanilla JS; this way, you can even asynchronously add more of those elements and don't need to add events every time you add a custom select box. Obviously, using a toolkit or framework like React, Angular, Vue or similar, you get the events basically for free.

Collapse
 
isfotis profile image
Fotis Papadogeorgopoulos

As an addition, when recreating some of the native (or more complex) interactive widgets, I find the WAI-ARIA Authoring Practices a great read.

They offer a list of the expected behaviour (for focus, keyboard etc.), as well as the roles and examples of alternative implementations.

iirc, <select> in this case falls under a "Listbox" pattern

For example, it lists that the ul is the one to receive focus, and that the li items are not tabbable per se, but aria-activedescendant on the ul marks the candidate selection. There different ways to do things, of course, which is why I like documents there. The discussion of those patterns on Github has also taught me much about accessibility :)

Something else that I am reminded of, Scott Jehl from Filament Group had an article a while back about styling the native select element. It has some fun stuff:
filamentgroup.com/lab/select-css.html

(There was a comment below that mentioned all this replacement seeming tedious, but I find it fascinating how much gets exposed to assistive tehcnologies out of the box)

Collapse
 
lexlohr profile image
Alex Lohr

One more thing I forgot to mention. You should also ensure that the functionality on different screen sizes works. Imagine a country list with 100+ countries and a web app that cuts of at the bottom of the screen. A maximum height of your options list with overflow: auto; will help, but doesn't solve all edge cases.

Collapse
 
lkopacz profile image
Lindsey Kopacz

oh i like the addition of roles here.

Collapse
 
equinusocio profile image
Mattia Astorino • Edited

Nice article! I have a question... why do you do this invasive customisation?

Why recreating a custom element that behaves like the native one, inteoducing a lot of code, possible bugs, accessibility issues (keyboard navigation and type filter) that is not integrated with the os (like on mobile)? Does this element deserve all of this time just to have a "custom style"? Is this really an accessibility issue?

With a custom select you will completely lose the native datalist element (which is being imemented by all browsers) and you also have to recreate the optgroup element.

I personally prefer my os integrated dropdown let the platform doing the rest.

Regards.

Collapse
 
emmabostian profile image
Emma Bostian ✨

Hi, so normally we would use the native HTML elements and style them appropriately. And if this were a side project, I'd use the browser styling. But often when building a design system, you use custom elements to convey your design language and branding. Thus, custom-styled elements are necessary. That's why I made this!

Collapse
 
equinusocio profile image
Mattia Astorino • Edited

But often when building a design system, you use custom elements to convey your design language and branding. Thus, custom-styled elements are necessary. That's why I made this!

I think this is true when you add functionalities to the custom component, but it's not true if you are replicating the exact behaviour by just adding a custom dropdown. This is a sort of "personal taste" that cause a lot of issues to developers and designers. In my opinion such element is a critical one and it should deserve the right considerations. Also i don't think a custom drop down will invalidate a design system effectiveness, this means that you should have and use the native select, and then make a new custom component to that add more funcionalities other than datalist, multiple select and optgroup (that coming with the native select).

BTW that's just my opinion. :)

Thread Thread
 
emmabostian profile image
Emma Bostian ✨

If you add the proper accessibility considerations, screen reader capability with ARIA and keyboard navigation with tabindex, it's just as accessible as the native element. So there's no accessibility benefit over the native element if they're equal in terms of interaction. If I can make a more visually appealing element that adds value and personality to my app, while ensuring compliance with the W3C standards, I'm going to do it.

Thread Thread
 
equinusocio profile image
Mattia Astorino • Edited

There are just a lot of things to consider/recreate:

  • Keyboard navigation (arrows and tabs) and activation (enter)
  • Select status (disabled)
  • Options status (disabled, selected, hidden)
  • User accessibility preferences (high contrast, reduced-transparency and reduced-motion)(Check this demo)
  • Options group element (<optgroup>)
  • Dropdown reposition based on the distance from window edges.
  • Dropdown vertical overflow with a long list
  • Mobile usability (custom select are almost always unusable on smarphones)
  • Fieldset element integration...
  • Multiple selection

All of these thing must be recreated. A lot of effort and possible breaking bugs just to add a custom experience (not better for all) and a custom style. In design systems these things are considere UX breaking.

BTW, i agree with you that this operation must be done if it provide a real value, but at this point, it's not a custom dropdown, it's just a new component. 👍🏻 Cheers.

Collapse
 
niorad profile image
Antonio Radovcic

As soon as you need integrated search, or icons on the options, the native select won't do. And sometimes you can't talk the client out of it.

Maybe this would work well as Web-Component, also.

Collapse
 
equinusocio profile image
Mattia Astorino • Edited

Maybe this would work well as Web-Component, also.

Yes this is a classic use case for web components, and if you can't make a web component you should just use both native and custom elements. BTW You can already make a native select with search using the <datalist> element that is being implemented.

jsfiddle.net/equinusocio/yj9fb7Lx/4/

Collapse
 
gabrielecimato profile image
Gabriele Cimato • Edited

This is impressive work! To be honest it makes me think that building an accessible drop-down shouldn't require all this work. I would hope to just need to add a few things here and there. Nonetheless this is next level! Very well done!

Collapse
 
link2twenty profile image
Andrew Bone • Edited

Amazingly, I'd been thinking of a way to do this too. I didn't get too far but had a 'proof of concept' build done.

My version has a long way to go though 😅

Collapse
 
emmabostian profile image
Emma Bostian ✨

Great job!! :)

Collapse
 
anduser96 profile image
Andrei Gatej • Edited

Hi Emma! Thank you for sharing this, it's very useful!

Here's a quick tip I'd like to share.

Instead of writing document.querySelector()/document.querySelectorAll() multiple times, you could do this:

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);

const list = $(".dropdown__list");
const listContainer = $(".dropdown__list-container");
const dropdownArrow = $(".dropdown__arrow");
const listItems = $$(".dropdown__list-item");

Enter fullscreen mode Exit fullscreen mode
Collapse
 
danielleeriksen profile image
Danielle Eriksen

I found this article searching for 'accessible custom dropdowns' and was extremely disappointed to find that it seems to be a blatant copy of this article here, morioh.com/p/0993e06398a1 I know development is all about reuse and sharing solutions, but credit should go to the original author for this content

Collapse
 
weo3dev profile image
william

Fun fact, the article you say that THIS article is a copy of, has a link to CodePen at the bottom that... wait for it... links to Emma's CodePen. So this is the OG, the other is the copy.

Collapse
 
gustvandewal profile image
Gust van de Wal

The Morioh article literally links back to here...

Collapse
 
cuginoale profile image
Alessio Carnevale • Edited

Thanks for sharing this Emma, a few notes from my side I hope you'll find useful.
Setting max-height and opacity makes the options not visible, but this doesn't mean they are not available to ATs or KB users. Try using the tab key (or a screen-reader) and you will see that those no-longer-visible options can still receive focus and are actually there.
You can fix this using display: none or setting aria-hidden: true.
I always test my work using a screen-reader (voiceover) and pretty much all the times I am amazed by the amount of extra work I need to do to make it more user-friendly.
Good to see people taking accessibility seriously!
Keep up with the good work

Collapse
 
lkopacz profile image
Lindsey Kopacz

Yay! I love this! I'm going to play around with this as well and I'll let you know what I find!

Collapse
 
emmabostian profile image
Emma Bostian ✨

Yasss thanks!

Collapse
 
noctivityinc profile image
noctivityinc

This is nice work, except, it doesnt appear to work if the list has an overflow-y: scroll and is longer than the height of the div. Also, it seems to scroll the entire page as well as the list. Anyway to prevent that?

Collapse
 
richardrazo profile image
Richard (Ricky) Razo • Edited

Overall nice work. I like your demo but find it odd that the arrow isn't clickable. I can see this being frustrating to disability users who either use a grid overlay to drill down on an item to be able to click on it, or even those with visual impairments that have spotty dark spots (from diabetes or what not) = so having more of the element clickable would be a plus.

dropdown__selected {

 padding-right: 22px;
 /*max-width: 80%;*/ <- remove

}

Collapse
 
locness3 profile image
locness3

You cannot place a div inside of a select.

Well, it seems this works on iOS WebKit.

Collapse
 
wtaylor45 profile image
Will Taylor

Awesome! I'll be honest, when I come up with some solutions like this I forget about a11y. Really really important stuff to consider, and you do it beautifully here.

Collapse
 
surajsharma profile image
Suraj Sharma

hmmm interesting but what if you want it to be searchable?

Collapse
 
emmabostian profile image
Emma Bostian ✨

That would be a combobox, not a select dropdown.

Collapse
 
niorad profile image
Antonio Radovcic • Edited

You can add a search-field as first LI and add some JS to filter out the other LIs by its value.

Collapse
 
skbhardwaj profile image
Shrikrishna BHARDWAJ

Hello Emma,

Great post indeed.
Just wanted to know, how would you close the dropdown if we click outside of the dropdown without selecting any option?

Thanks,

Collapse
 
kevsingh98 profile image
kevsingh98 • Edited
function _clickOutside() {
   document.addEventListener("click", function (event) {
     if (event.target.closest(".dropdown")) { //replace the class name to your needs
     return;
     }
     close(); //just calling a function that removes classList of isOpen
     }, false);
}

Hope this can help :)

Collapse
 
naumankhan profile image
Nauman Ahmed Khan

If we have 2 or more dropdowns on single page its not working