VueSelect: Accessible Vue.js component


HTML provides a variety of input elements. <select> allows users to select one of many options. Accessibility tools like screen readers readily recognize these “native” input elements. However the downside of these elements is they are not fully customizable across browsers and platforms. Not being able to customize will be a problem if one is working a site that follows a specific design language like Material Design. One way around this limitation would be implement your own widget. This blog post is a walk-through of implementing such a widget as a Vue.js component which is accessible as well. Inspiration for this component MDN’s guide to implementing a custom widget.


Before we begin let us define our Minimum Viable Product for our component.

  • Behaves similar to <select>
  • No dependencies apart from Vue.js
  • Keyboard accessible
  • Screen reader accessible

Enough talk, show me the code! Code for this component can be found on github at and a simple demo can be found here.

HTML structure

The basic skeleton structure for our widget is very simple.

The outer <div> acts as the container of the component which houses a <span> and a <ul>. We will use the <span> to show the selected option and use the <ul> to show the options.

Styling the component

Now that we have our HTML structure in place, let us start customizing our component. For this I am using BEM naming convention and SCSS.

First thing to note is the scoped attribute of the style tag. Vue.js takes care of keeping the styles defined by this component are local to this and does not leak out and conflict with others. One of the first things that we do is define some variables and reset the browser defaults for margin, padding, box-sizing, and user-select. Now we start defining CSS classes that we will for our component.

First class that we define is .select__dropdown. This will be assigned to our container <div>. We remove the outline set the position attribute to relative, this will allows us to position the list of options. Next we define the chevron for our drop down using the pseudo-selector :after using the classes .select__dropdown--open, and .select__dropdown--close. CSS-Tricks has a nice article on how to create triangles. Next we will define the styling for the <span> using the class .select__value. When our component is in focus we want to provide visual feedback, this is accomplished via the pseudo-selector :focus. .select__optionlist defines the styling for the list container <ul>. We set the z-index to a high value so that it overlaps the down arrow. Moving further we define the closed state of the drop down list using .select__optionlist--close where we set the visibility to hidden. We are using the attribute visibility instead of display is to support accessibility. To style the individual options we will define the class .select__option. We use the :hover pseudo-selector to provide visual feedback for mouse hover. When a certain option is selected we hightlight that using .select__option--selected.

Now our HTML should look like this.

Making it all work

Mouse and keyboard events that we have to handle to make our component to be functional are:

  1. onClick of the container <div>
  2. onClick of the options
  3. Space, Enter, Up arrow, and Down arrow keyboard events on the container <div>

We also have to add Accessibility attributes to our markup. These attributes enables screen readers to interpret VueSelect component correctly. Component after adding the event listeners and accessibility attributes should look something like this.


VueSelect supports two properties, value of type String and items and array of Strings. As the name suggests, value is the property that the parent component will pass to us with the preselected value or an empty string and items are the options that the user can select from. Whenever user selects an option we will emit two events input and update:value. Custom event input is straight forward to understand. Second event update:value is emitted to support the new Vue 2.3.0+ feature the .sync modifier.

Component State

To orchestrate our component we need these data properties:

  • isOpen — denotes if the drop down is open or closed.
  • mutableValue — the current selected value.
  • selectedIdx — index within the items property array.
  • hoverIndex — maintains the state of option on which the user is “hovering” when navigating via the keyboard.

Completed code

The completed code for this component can be found on Github at Note that this component is not production ready, this is something I wrote to understand how to implement accessible elements in Vue.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s