Skip to main content
May 5, 2022

Styling `select` elements for real

Styling <select> elements consistently across browsers has long been a difficult task. While it is now possible to style the button part of a <select>, styling its contents – the box that contains the list of options, and the options themselves – remains very limited.

These limitations often push web developers to use libraries that provide custom select-like controls. Unfortunately using a library comes with important implications: including it in your project increases complexity, your customers will need to download the library code on each visit, and the custom select might not support keyboard navigation or the same accessibility semantics that built-in <select>s do.

Screenshot of a select element containing a list of French cities. The select element is styled in a way that currently cannot be achieved with the built-in select HTML element and CSS. The options have borders around them, icons next to them, and they are organized in a grid with 4 columns.

In this article, we’ll review the current state of styling <select> elements and what limitations still exist, and we’ll introduce a new and experimental element which should, over time, overcome these limitations once and for all.

But before we dive in, let’s review the three main parts of a <select> element:

  • Button: this part is visible to users before they interact with the <select> and contains the currently selected value and a disclosure icon.
  • Listbox: this part appears when the button is selected and serves as a container for the options.
  • Options: these parts are displayed in the listbox and represent the choices users can make.
A chart explaining what the different parts of a select element are. It shows the button part at the top, which contains the selected value text, and an icon to open the list of options. Below this, the listbox part is shown, containing a list of options, each with a value.

I’ve left out the optgroup part from this list and diagram for simplicity reasons. Let’s not worry about it in this article. Also, the part names I’m using here are not meant to match the actual accessibility semantics of the <select>. They’re just easy to understand.

With this out of the way, let’s review how the various parts can be styled today.

Styling the button part

Doing this is pretty easy nowadays. We can apply CSS properties to the <select> element and change its background color, borders, font, size, margin, padding, and more.

With this, we can pretty quickly go from the default style of the <select> element (on the left in the image below) to something more visually pleasing and which might integrate better with the rest of our website (on the right in the image below):

Two select elements, we only see the button part, not the drop-down with the options. The first one on the left has the default browser styling. The second one on the right has been custom-styled with CSS. It has a bigger font, more padding, a different background, and borders.

Even better, styling the button part works very well across browsers. Back in 2018, the Filament Group design studio published Styling a Select Like It’s 2019, a blog post and CSS code snippet that showed how to style a <select> across all the main browsers that existed at this time. Now, with Internet Explorer’s retirement around the corner, it’s even easier to get <select>s to look the same across browsers.

Styling the listbox part

Sadly, there’s no way for web developers today to style the listbox. There simply is no way to target this part of the <select> with CSS.

As a result, we can’t change the default shadow, border, corners, background color, or padding of this box to match the rest of our website.

It’s important to realize that this part does play an important role in how a <select> looks in a web page. Let’s take a look at an example in Microsoft Edge (on the left) and Firefox (on the right), both on Windows 11:

Two select elements, with default browser styles, and 3 options. The first select on the left is rendered by Edge, with a large and blurry drop-shadow and large rounded corners. The second select, on the right, is rendered by Firefox, and has a sharper and smaller drop shadow, and smaller rounded corners.

As this example shows, the listbox part comes with a few things you might want to customize to your needs:

  • the way the shadow drops on the rest of the page might not work well with the rest of your styles,
  • the color, thickness, and rounding of the border might feel a bit out of place as well.

Depending on the browser, and operating system, the listbox part may be implemented with internal user-agent shadow DOM, or directly by the OS, none of which can be targeted by our CSS unfortunately.

Styling the option part

For this final part, things are a bit better. Several CSS properties can be used to change the appearance of an option, and some properties applied to the <select> element are inherited too.

For example, it’s possible to change the color and font of the <option> elements to match your website’s appearance. You can even give each option its own background color for example.

A select element showing a list of French cities, where each option in the drop-down having a different background color.

Unfortunately, only a small subset of CSS applies to options. Today, in Microsoft Edge (and any other Chromium-based browser), only the following CSS properties are taken into account:

  • background-color
  • color
  • direction
  • font
  • font-family
  • font-size
  • font-style
  • font-variant
  • font-weight
  • text-align
  • text-transform
  • visibility

This is a good list, and it should be sufficient in most cases. But many interesting effects will not be possible to achieve. As an example, adding horizontal borders between options isn’t possible, and so is adding padding in options to give them more room to breathe. One of the most requested feature is the ability to add images next to options, and this is sadly not possible either since the background-image property doesn’t work. One other example of an interesting effect is using a grid or a flex layout to display the options in a different way. Sadly this is also not possible.

Enter the <selectmenu> element

There’s good news! Some time ago, a cross-company group of people got together ago and formed Open UI. Their goal is to create specifications for much more extensible and stylable UI controls on the Web. And then work with standard bodies and browser vendors to get them implemented. This is a part of the Web that has, historically, been left out of standardization, and up to browser implementations to deal with on their own. So it’s a very exciting project that aims to fill one of the major remaining gaps on the Web platform today. Also, the people at Open UI are great, and you can join them to follow along or even push some of the proposals!

Part of the work the group has done so far is already starting to come to fruition. The Microsoft Edge team has started to implement a new HTML element in Chromium, in collaboration with Google, based on the Open UI <select> spec: the <selectmenu> element. This element is a new implementation of a select control, but fully stylable with CSS, and much more extensible too.

The element is only available in Chromium-based browsers for now, and to use it, you’ll have to switch the “Experimental Web Platform features” flag in about://flags first.

Using <selectmenu>, and a few lines of CSS code, you can create designs like these two examples below:

Two examples of a styled selectmenu element. The first one shows a list of browsers. The button part is styled with light grey borders and rounded corners. The listbox part has a light drop-shadow, and the options in the listbox have browser icons next to them. The second example contains a list of French cities as options. The options are organized in 2 vertical columns. Borders are drawn between options. And little square icons are displayed next to each option.

Notice the custom borders and shadows, the separators between options, the images, and custom layout.

Here is part of the HTML and CSS code that was used for the demo on the right, with the list of cities:

<style>
  selectmenu::part(button) {
    border: 2px solid #aa99a0;
    border-radius: .15rem;
    padding: .6rem;
  }

  selectmenu::part(listbox) {
    background: #aa99a0;
    box-shadow: 0 0 .3rem 0 #0003;
    margin-top: .3rem;
    border: 2px solid #aa99a0;
    border-radius: .15rem;
    padding: 0;
    display: grid;
    grid-template-rows: repeat(10, 1fr);
    grid-auto-flow: column;
    gap: 2px;
  }

  selectmenu option {
    padding: .6rem;
    background: white;
  }
</style>
<selectmenu>
 <option value="Paris">Paris</option>
  <option value="Marseille">Marseille</option>
   ... 
</selectmenu>

The new <selectmenu> element lets you style every aspect of the different parts of the control. As a developer, you get access to it all, and are free to define your own styles, while being certain that the browser will take care of things like positioning the popup, handling keyboard access, and wiring the right accessibility semantics.

If you want to learn more about Open UI, the <selectmenu> element, and how you can help with this effort, you can read my longer article about it: Say Hello to selectmenu, a Fully Style-able select Element, on CSS Tricks.

Keep in mind, however, that this is very new and experimental. The spec and implementation will most definitely change as the result of ongoing discussions.

But why does this matter?

You might argue that styling the color and font of options is more than enough, and anything more is not really necessary. After all, a select is made for users to quickly choose an option from a list and that’s it. If you, as a web developer, cannot style this part of your user flow and instead are forced to rely on what the browser and OS provide, then at least your users will have a familiar and optimized experience.

Take iOS for example, the iPhone and iPad operating system, options on this platform can’t be styled at all, even choosing a different font or color doesn’t work, for good reasons. As the available room may be limited on the device, it’s great that the OS can take over the rendering, positioning, sizing, and styling of the list of options for users to effectively make their choice in a familiar UI.

So, if you need your users to choose from a list of options (and if other solutions like a bunch of radio buttons aren’t feasible), using a native <select>, and accepting its default design, is a great option!

But, even if you agree with the above, and I hope you do, I believe a new element like <selectmenu> is still necessary. Here’s why: people, still today, build or use custom select elements purely based on aesthetics. Design decisions, not accessibility ones, still lead many web development businesses to roll out their own custom selects. As long as they do, less accessible experiences will continue to exist, complex code will need to be maintained, and heavier bundles will need to be downloaded by users.

If you were about to start creating your own custom select, I suggest you read Sarah Higley’s excellent 2 part blog post, “Select your poison”, about it first.

So having a fully stylable select element on the web is an important step in the right direction. It will allow web developers to define the extra styles they need and provide a coherent design, while preserving accessibility and simplicity.

There’s a progressive enhancement story to this as well. The new <selectmenu> element will likely not be available everywhere for a long time and might not ever be as stylable on mobile than on desktop. That’s ok, your website could detect its availability (perhaps using User-Agent Client Hints or checking HTMLSelectMenuElement in JavaScript at runtime), and only use it when possible, letting the browser and OS decide whether to apply your CSS styles. If all you use the <selectmenu> element for is styling its various parts like in the example before, then it works just like the <select> element, and it’s possible to switch one with the other.

Note, however, that at the time of this writing the <selectmenu> element also allows web developers to replace the entire user agent shadow DOM and instead insert their own custom markup. This means it is possible to not only replace the button, listbox, and options parts, but also add extra elements around or within them. This is a super power that can make it possible to create a lot of functionality that the existing <select> can’t achieve today, but that people have wanted for a while, like a combobox.

Doing this, however, means that the browser might no longer be able to guaranty the accessibility of the control like it can today with a normal <select>, and it also means that you wouldn’t be able to switch between a <select> and <selectmenu> element as easily.

Closing notes

As we’ve seen, styling selects on the web isn’t as bad as it used to be in the past. Using the HTML <select> element gets you very far already, is accessible, way cheaper than using a custom library, and familiar to your users.

And now, with the work that Open UI and Microsoft are doing, there’s an even brighter future ahead of us. One where we can style the contents of our selects just like we’ve always wanted, without the added complexity.

Are there other things you wish the Web could do? Let browser vendors know, submit your ideas on The Web We Want!

– Patrick Brosset, Senior Product Manager, Microsoft Edge