For most frontend developers, the phrase "custom form elements" is something that belongs in nightmares. No element that is put in a form is spared it's out-of-the-box look. One of the abused form elements, <select>, is typically replaced in HTML forms with completely customized dropdown markup that is tied together with Javascript. This isn't optimal for accessibility and is a pain for maintainability. Here we'll discuss the ways we can create customized <select> elements.


Let's first discuss quickly what this <select> element should be doing in our markup, W3 has a succinct definition:

The <select> element represents a control for selecting amongst a set of options.

Simple enough. Why then do we have the need to extend what the browser gives us for free? It boils down to overriding the existing styling, but in the interim the implementation becomes muddled. The need to redesign is usually one of two things: attempting to standardize the design cross-browser (as the form control looks different on each browser) or adding more than plain text to the set of options that are available (embedding images in an option). Both are valid concerns from a UX perspective. Let's jump into the ways we can style the element and make developers and designers happy.


Here we'll enumerate the possible ways to style the <select> element. Take note that this becomes a balancing act, we will have to trade obstrusive markup for consistency in the design.

Use only supported CSS properties

One way to style <select>s is to style only the properties that are supported on the element. This is the way many CSS kits such as Bootstrap implement a customized select menu. Some of the supported properties are background-color, border-radius, and border. That's what most of us would want and it doesn't take much. The upside is that there is no additional markup for developers to rely on and it will just work. The rule could look something like this:

select {
    border: 1px solid #ddd;
    background-color: #fff;
    border-radius: 5px;

The downside with this for many people is that there is no gradient support for the background and the arrow looks different on every single browser, for many designers this inconsistent experience is unacceptable:

Use pointer-events for the arrow

One of the ways we can cover the arrow is using the pointer-events CSS property. What exactly is that?

The CSS property pointer-events allows authors to control under what circumstances (if any) a particular graphic element can become the target of mouse events.

This means that if we apply the none value to an arrow we absolutely position, the user will still be able to click on the select element while it's being obscured by a custom arrow. How does this look in the CSS? We simply need to add a psuedo-element after the <select> that is positioned on the right side of the select with the pointer elements off.

select:after {
    pointer-events: none;
    content: "V";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;

The downsides to this approach are that pointer-events is not supported on IE and we still can't style beyond the supported properties.

Add an overlay element to the select

The above two options are great, they get us a custom styled event and an arrow that is the same across all the browsers! But what if we want a gradient on the background or full control over every pixel on the form element? Now we get into adding additional markup that will add an overlay that looks like a select and pass it's events to the form element instead of the overlay.

We will have three main components: the outer container, inner container and the select element itself.

  • Outer container: will be setting the position relative so that absolute positioning works within the child container
  • Inner container: will be containing the select element and cutting off the overflow by being slightly thinner than the select (to hide the Firefox arrow that won't go away with appearance:none)
  • Overlay element: will provide the styling for the element and position it over the select box (the arrow will be a pseudo-element on the overlay)

It's a bit more CSS than would fit inline in this post, here's a link to a CodePen here demonstrating this technique.

Additional optimizations

After taking a wide survey of the <select> elements used across major websites, they have all fall into the general buckets above. One of the nice optimizations I found along the way was used on Square Dashboard, where instead of offsetting the width of the Firefox arrow, they use the text-overflow to trigger an overflow that will push the arrow out of the container. The additional property-value pairs look like this:

select {
    text-overflow: "";
    text-indent: 0.01px;

Closing Thoughts

Hopefully this was a conclusive post on styling <select> elements, the one thing to remember is that HTML forms should always be using the browser's built in elements, anything that is thrown together with Javascript will be a suboptimal user experience for accessibility and yet another thing that needs to be maintained.

Let me know if there is anything else to add, reach out to me @mduvall_.