Striking a Balance Between Native and Custom Select Elements

nc efi placeholder

Here’s the plan! We’re going to build a styled select element. Not just the outside, but the inside too. Total styling control. Plus we’re going to make it accessible. We’re not going to try to replicate everything that the browser does by default with a native element when any assistive tech is used. But when a mouse is being used, we’ll show the styled version and make it function as a select element.

That’s what I mean by “hybrid” selects: they are both a native ) that displays a list of options for the user to select within a form.

Deciding what type of dropdown we’re talking about can be a foggy task. Here are some examples from around the web that match how I would classify those three different types. This is based on my research and sometimes, when I can’t find a proper answer, intuition based on my experience.

image 9
Dropdown-land: Five scenarios where different dropdowns are used across the internet. Read the table below for a detailed description.
Diagram Label Scenario Dropdown Type
1 The dropdown expects a selected option to be submitted within a form context (e.g. Select Age) Select
2 The dropdown does not need an active option (e.g. A list of actions: copy, paste and cut) Menu
3 The selected option influences the content. (e.g. sorting list) Menu or Select (more about it later)
4 The dropdown contains links to other pages. (e.g. A “meganav” with websites links) Disclosure Navigation
5 The dropdown has content that is not a list. (e.g. a date picker) Something else that should not be called dropdown

Not everyone perceives and interacts with the internet in the same way. Naming user interfaces and defining design patterns is a fundamental process, though one with a lot of room for personal interpretation. All of that variation is what drives the population of dropdown-land. 

There is a dropdown type that is clearly a menu. Its usage is a hot topic in conversations about accessibility. I won’t talk much about it here, but let me just reinforce that the

element is deprecated and no longer recommended. And here’s a detailed explanation about inclusive menus and menus buttons, including why ARIA menu role should not be used for site navigation.

We haven’t even touched on other elements that fall into a rather gray area that makes classifying dropdowns even murkier because of a lack of practical uses cases from the WCAG community.

Uff… that was a lot. Let’s forget about this dropdown-land mess and focus exclusively on the dropdown type that is clearly a

Styling form controls is an interesting journey. As MDN puts it, there’s the good, the bad, and the ugly. Good is stuff like

which is just a block-level element to style. Bad is stuff like checkboxes, which can be done but is somewhat cumbersome. is the most frustrating form control by far, mainly because of its lack of styling support. The UX struggle behind it is so big that we look for other alternatives. Well, I guess the first rule of , period.” But let’s face reality: a select is still our best solution in a number of circumstances. That might include scenarios where we’re working with a list that contains a lot of options, layouts that are tight on space, or simply a lack of time or budget to design and implement a great custom interactive component from scratch.

Custom element comes with more features we get for free and aren’t included in those requirements, such as:

  • The checked option is perceivable for all users regardless of their visual abilities.
  • The component can interact with a keyboard in a predictable way across all browsers (e.g. using arrow keys to navigate, Enter to select, Esc to cancel, etc.).
  • Assistive technologies (e.g. screen readers) announce the element clearly to users, including its role, name and state.
  • The listbox position is adjusted. (i.e. does not get cut off of the screen).
  • The element respects the user’s operating system preferences (e.g high contrast, color scheme, motion, etc.).

This is where the majority of the custom selects fail in some way. Take a look at some of the major UI components libraries. I won’t mention any because the web is ephemeral, but go give it a try. You’ll likely notice that the select component in one framework behaves differently from another. 

Here are additional characteristics to watch for:

  • Is a listbox option immediately activated on focus when navigating with a keyboard?
  • Can you use Enter and/or Space to select an option?
  • Does the Tab key jump go to the next option in the listbox, or jump to the next form control?
  • What happens when you reach the last option in the listbox using arrow keys? Does it simply stay at the last item, does it go back to the first option, or worst of all, does focus move to the next form control? 
  • Is it possible to jump directly to the last item in the listbox using the Page Down key?
  • Is it possible to scroll through the listbox items if there are more than what is currently in view?

This is a small sample of the features included in a native behaves differently across browsers and screen readers. Once we decide to create our own custom select, we are forcing people to use it in a certain way that may not be what they expect. That’s a dangerous decision and it’s in those details where the devil lives.

Building a “hybrid” select

When we build a simple custom select, we are making a trade-off without noticing it. Specifically, we sacrifice functionality to aesthetics. It should be the other way around.

What if we instead deliver a native select by default and replace it with a more aesthetically pleasing one if possible? That’s where the “hybrid” select idea comes into action. It’s “hybrid” because it consists of two selects, showing the appropriate one at the right moment:

  • A native select, visible and accessible by default
  • A custom select, hidden until it’s safe to be interacted with a mouse

Let’s start with markup. First, we’ll add a native            

This takes us to styling, where we not only make things look pretty, but where we handle the switch from one select to the other. We need just a few new declarations to make all the magic happen.

First, both native and custom selects must have the same width and height. This ensures people don’t see major differences in the layout when a switch happens.

.selectNative,
.selectCustom {
  position: relative;
  width: 22rem;
  height: 4rem;
}

There are two selects, but only one can dictate the space that holds them. The other needs to be absolutely positioned to take it out of the document flow. Let’s do that to the custom select because it’s the “replacement” that’s used only if it can be. We’ll hide it by default so it can’t be reached by anyone just yet.

.selectCustom {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
}

Here comes the “funny” part. We need to detect if someone is using a device where hover is part of the primary input, like a computer with a mouse. While we typically think of media queries for responsive breakpoints or checking feature support, we can use it to detect hover support too using @media query (hover :hover), which is supported by all major browsers. So, let’s use it to show the custom select only on devices that have hover:

@media (hover: hover) {
  .selectCustom {
    display: block;
  }
}

Great, but what about people who use a keyboard to navigate even in devices that have hover? What we’ll do is hide the custom select when the native select is in focus. We can reach for an adjacent Sibling combinatioron (+). When the native select is in focus, hide the custom select next to it in the DOM order. (This is why the native select should be placed before the custom one.)

@media (hover: hover) {
  .selectNative:focus + .selectCustom {
    display: none;
  }
}

That’s it! The trick to switch between both selects is done! There are other CSS ways to do it, of course, but this works nicely.

Last, we need a sprinkle of JavaScript. Let’s add some event listeners:

  • One for click events that trigger the custom select to open and reveal the options
  • One to sync both selects values. When one select value is changed, the other select value updates as well
  • One for basic keyboard navigation controls, like navigation with Up and Down keys, selecting options with the Enter or Space keys, and closing the select with Esc

Usability testing

I conducted a very small usability test where I asked a few people with disabilities to try the hybrid select component. The following devices and tools were tested using the latest versions of Chrome (81), Firefox (76) and Safari (13):

  • Desktop device using mouse only
  • Desktop device using keyboard only
  • VoiceOver on MacOS using keyboard
  • NVDA on Windows using keyboard
  • VoiceOver on iPhone and iPad using Safari

All these tests worked as expected, but I believe this could have even more usability tests with more diverse people and tools. If you have access to other devices or tools — such as JAWS, Dragon, etc. — please tell me how the test goes.

An issue was found during testing. Specifically, the issue was with the VoiceOver setting “Mouse pointers: Moves Voice Over cursor.” If the user opens the select with a mouse, the custom select will be opened (instead of the native) and the user won’t experience the native select.

What I most like about this approach is how it uses the best of both worlds without compromising the core functionality:

  • Users on mobile and tablets get the native select, which generally offers a better user experience than a custom select, including performance benefits.
  • Keyboard users get to interact with the native select the way they would expect.
  • Assistive Technologies can interact with the native select like normal.
  • Mouse users get to interact with the enhanced custom select.

This approach provides essential native functionality for everyone without the extra huge code effort to implement all the native features.

Don’t get me wrong. This technique is not a one-size-fits-all solution. It may work for simple selects but probably won’t work for cases that involve complex interactions. In those cases, we’d need to use ARIA and JavaScript to complement the gaps and create a truly accessible custom select.

A note about selects that look like menus

Let’s take a look back at the third Dropdown-land scenario. If you recall, it’s  a dropdown that always has a checked option (e.g. sorting some content). I classified it in the gray area, as either a menu or a select. 

Here’s my line of thought: Years ago, this type of dropdown was implemented mostly using a native  is a type of menu. Both have similar semantics and behavior, especially in a scenario that involves a list of options where one is always checked.  Now, let me mention the WCAG 3.2.2 On Input (Level A) criterion:

Changing the setting of any user interface component should not automatically cause a change of context unless the user has been advised of the behavior before using the component.

Let’s put this in practice. Imagine a sortable list of students. Visually, it may be obvious that sorting is immediate, but that’s not necessarily true for everyone. So, when using ...

That said, using a elements look and behave differently across browsers.

The “hybrid” select is just another attempt to create a good looking select while getting as many native features as possible. Don’t look at this technique experiment as an excuse to downplay accessibility, but rather as an attempt to serve both worlds. If you have the resources, time and the needed skills, please do it right and make sure to test it with different users before shipping your component to the world.

P.S. Remember to use a proper name when making a “dropdown” component. 😉

Source link

Leave A Reply