Two levels of customising <selectmenu>

The proposed <selectmenu> will have powerful styling options and full control over the different parts. In other words, there would be not one, but two levels of customisation. In this post, we'll look at what they are and when they are useful.

<selectmenu>, you ask? It's a proposed new HTML element that is like <select>, but fully customisable. Why? Because so many people are building their own selects, from scratch or with libraries, and that should be easier to do. With accessible defaults where possible and by leaving the parts that browsers are good at to the browser (like knowing where to position it, to place it on the top layer or to automatically dismiss it when appropriate (‘light dismiss’)).

For context, <selectmenu> is a proposal by the Open UI Community Group. This group was founded to study common components and controls that people build on the web (in design systems, see the component name matrix), and suggest Web Platform features to make it easier to build such controls. Think new HTML elements, CSS features or APIs, as well as things that surface across HTML, CSS and JS.

Open UI homepage

<selectmenu> doesn't ‘exist’ just yet, it is an idea that is being prototyped in Chromium, with other browser engines currently not opposed, but also not implementing it (as far as I'm aware). While you would't use it in projects yet, you can experiment with it in Chrome or Edge Canary with Experimental Web Platform Features flag on, and file issues if something doesn't work as expected or desired. For brevity, this post will refer to it as if it exists, to avoid saying ‘would’ in every sentence.

The anatomy of a <selectmenu>

The <selectmenu> element, as currently prototyped, is a lot like the <select> element that we've had for years. We can use it in a form, users can select an option out of many and their choice becomes part of the data that we process.

But where a <select> comes with free built-in styles that we can't change, <selectmenu> consists of some explicitly designed parts that can be changed (more on that a later). Often, the browser built-in styles of a <select> are just fine, sometimes they are not.

example of the parts described in this section, the example is a WYSIWYG style dropdown where the options for heading types have different sizes In Sanity's Portable Text editor, the user can choose text types. Each option is styled like something of that type is commonly styled, and there is some shadows, rounded corners and arrows that a regular <select> can't have.

The parts are:

  • select, the root element, wrapping/containing all the parts
  • button, the toggle/button the user activates to open the options
  • listbox, the part that is toggled, it wraps the options
  • optgroup, a part that we can optionally use to group related options together
  • option, an actual choice, a value that a user can pick
  • selected-value, the element that displays the currently selected value

(from: Anatomy of the selectmenu)

When using regular <select>s, it is possible to make the button part look however we want. In some browsers, we can also apply some specific styles to options, like background colours.

With <selectmenu>, these parts are not just parts in the colloquial sense, they are <part>s in the Web Component sense, meaning that they exist as elements in the shadow tree of the component. Consequently, all of these parts can be changed.

There are two levels to that: the first is to write CSS, where we can use all the power CSS has to offer, the second is to completely replace the DOM structure to what we want or need.

Option 1: CSS

In the <selectmenu> prototype, the options are part of the (light) DOM like they are in a <select> element, which means we can target them with CSS (and unlike with <select>, our styles will actually get applied).

Codepen screenshot with <option> elements in HTML, and styles applies by targeting with option--h1, option--h2 as the selector, plus a preview. You could even give each option its own style! See Codepen (view in Canary with Experimental Web Platform Features flag on)

This is the most basic example of styling a selectmenu: you apply CSS to the options, like you would to any other HTML element.

The other parts of the selectmenu are targeted differently: we'll use a ::part() selector referencing the respective part name. For example, to style the button:

selectmenu::part(button) {
  // add any button styles
}

(Codepen of example with styled options)

To style other parts, you use that part's name instead, for instance ::part(listbox). Just last week, a resolution was passed to also expose the arrow as a part, to allow for easy styling or icon-replacing (part name to be decided).

In case you, like me, had not used the ::part selector, it is part (no pun intended) of the CSS Shadow Parts module.

For background: Web Components can have a light and a shadow DOM. The light DOM is part of the DOM like anything else and can be styled, but the shadow DOM is internal to the element. This a bit like public and private functions. For styling purposes, the CSS Shadow Parts module states that the creator of a Web Component can choose to expose parts of their shadow DOM by names. Developers provide a list of parts (element names) to be exposed, and then the elements can be targeted through ::part(partname). With <selectmenu>, we get a browser built in Web Component that has parts exposed (except for option, which is in the light DOM).

This is huge… I mean, being able to throw CSS at the different parts of a selectmenu unlocks a lot of possibilities. Many of us will likely just use it to round some corners, add some shadows, maybe a little color or typography. And that's fine, we never could unless we built or loaded a library for custom selects.

I'm excited about being able to add a few finishing touches to selects, alone. But CSS has numerous powerful features, so that's only the surface. With all of CSS at our fingertips, custom selectmenu styles are probably going to be used in more interesting ways than we can think of now.

Option 2: full control by replacing parts

If we need more than ‘just’ styles, it is also possible to replace entire parts with whatever we like. For instance, we could write our own button entirely, and tell the browser to use that instead.

Replacing a part is done with slots. Let's say we created a button with markup of our choice:

<button>select option</button>

We can then tell the browser that, actually, we want this to be what the button part is, adding slot="button" and behavior="button":

<button 
  slot="button" 
  behavior="button"
>select option</button>

(Codepen of example with replaced button part)

Now, the button will be what's used for the part called ‘button’ (as it is slotted into the right place with slot) and get the browser's select button behavior and accessibility accomodations (set through behavior). We could do the same for the other parts.

We could also do this in JavaScript, with Element.attachShadow(), so that the markup we want to use can live in our JS, and we only need one HTML element at the point of usage rather than one plus a few more for slotting.

But wait… if we're replacing all the parts with our own things, isn't this just like rolling our own selects entirely? Well, yes, it is the advanced option, and usually CSS should suffice. But even if we replace some or many parts, the selectmenu would still get a bunch of things from the browser for free, like positioning, being on the top layer and light dismiss, features that would be hard to impossible to code in JavaScript.

Accessibility risk: avoid unexpected nests

There is an important accessibility risk to be aware of for folks planning to replace parts in their selectmenus. Browsers and assistive technologies have certain expectations about what HTML is and does. If we divert from those expectations, we risk causing real problems for end users, which could include controls becoming unusable.

One example is what would happen if we would nest an interactive element like a link or button inside an option. Maybe one of our options has a tooltip? All of this should be avoided, at least for the time being (as far as I understand, this may change when secondary actions become a thing, see also w3c/aria#1440). The reason is: assistive technologies expect just text inside of options, not controls, so this would break badly and it could mean users wouldn't find those buttons or links, or wouldn't be able to interact with them. For this reason, it is strongly recommended to avoid interactive elements inside of options, i.e. do not add links or buttons in options.

Summing up

In this post, we looked at two ways to customise the parts of a selectmenu element. We can use CSS, which should cover a large amount of use cases and offer flexibility and room for creativity. Or we can replace entire parts with whatever we would like, the more advanced option that has accessibility risks if not thoroughly checked against specs and implementations. For a lot of the use cases that I see in design systems I have worked on, I feel CSS alone will cover all the needs. And it will do so very elegantly, often in literally a few lines of code.

Again, this is all in the future, the element is just being tested out. But, it may well be that this same pattern of having both CSS and parts-replacement for control customisation will come to other Open UI work, too. Generally, I am a fan of this layered approach. As a developer, I like the principle of being able to make things as simple or complex as I want.

If you want to see more demos, the Microsoft Edge team has built a series of cool <selectmenu> demos(as they note, pushing the limits and some with accessibility issues). You can see and play with these, using Edge or Chrome Canary with Experimental Web Platform Features turned on. These demos include a lot of part replacements, too. If you have feedback, want to follow this work more closely or chime in, head to Open UI issues on GitHub or join us on the Open UI Discord community.

Thanks Patrick Brosset for feedback on an earlier draft, Greg Whitworth for the idea.

Comments, likes & shares (104)

Great read Hidde 👏 I'm aiming to write something up on selectmenu in Q3 when some things are ironed 🤞 I'm the Chrome DevRel working on <selectemenu> ftr so it's great to see when an article pops up! 🙌 (No pun intended on the popup 😅)
Thanks! And nice, looking forward to it (can't wait to see what kind of demos you'll do!), hope by then we'll be a bit further along with some of the accessibility work (and testing).
Yes! That's the part I've been holding off for mainly. Was talk of using <outgroup> like the escape hatch for things you want that can't go in <option>. Perhaps a footnote or sort, for example 🤔 Gonna be exciting to see all different things for sure!
good read! added to github.com/Fyrd/caniuse/i… B-) :) seems like neither @mozhacks nor @webkit have positioned themselves, have they? should we query the positions? 🤔
Can `<optgroup>`s be nested? A somewhat contrived example would be for something like a regional flag picker where you pick by continent (Europe), then country (France), then region (Provence).
These are the kind of things I'm trying to push with some of the demos 💯 Haha – Stop giving away my demo ideas Thomas 😅 I literally have that in my tracker for not only country selection, but like, timezone selection across a map etc. Seeing how you can push those things 🧐🤓
According to regular optgroup spec: no, can only contain options (or templates) html.spec.whatwg.org/dev/form-eleme… But since you can put whatever in your own shadow root, you could theoretically make it work (but would need to test well with assistive tech, likely inaccessible by default)
(if that's the spec, that's what AT will expect, and there's little we can change about this expectation without introducing new concepts that then need to get AT adoption (which goes much slower than browser support in my experience))
Yeah, it sounds like it should be possible to make this work, especially if the developer is in control. Looking forward to seeing you all explore our options here!
Yeah, there has been some discussion around what could be allowed, etc. It's interesting to know how this is going to work going forward. The thing is, for anything not "compatible" with a <selectmenu>, there will always be a workaround using a "popup" 🤔
sure, but if it's a thing that doesn't exist in accessibility APIs that would have the same issues
I believe some of those conversations are happening, I guess would not hurt to query if you have a process for that?
I‘d just ask in their respective position repos? :) - github.com/WebKit/standar… - github.com/mozilla/standa…
oh definitely, but as far as I understand there are some issues that aren't invented yet so JS wouldn't help there (like what `aria-actions` could become gist.github.com/smhigley/8dbe6…), hence the console warnings idea (that doc has some cool real world examples too btw)
This thread is saved to your Notion database. Tags: [Selectoption]
Two levels of customising <selectmenu> | hidde.blog hidde.blog/custom-select-…
#CSS #Automated | Two Levels of Customising <selectmenu> hidde.blog/custom-select-…
Two levels of customising <selectmenu> hidde.blog/custom-select-… #webdev #webdesign #CSS
Two levels of customising <selectmenu> | hidde.blog hidde.blog/custom-select-…
Two levels of customising <selectmenu> from @hdv hidde.blog/custom-select-…
🔴 Two levels of customising <selectmenu> by Hidde de Vries @hdv #css #html #webdev #selectmenu hidde.blog/custom-select-…
Two levels of customising <selectmenu> @hdv hidde.blog/custom-select-…
<selectmenu> doesn't exist just yet, it is an idea that is being prototyped in Chromium. You can experiment with it in Chrome or Edge Canary with Experimental Web Platform Features flag on hidde.blog/custom-select-…
Maybe you're thinking of this prototype in Chromium: hidde.blog/custom-select-…
Two Levels of Customising “<selectmenu>”, by @hdv: hidde.blog/custom-select-…
Two levels of customising <selectmenu> hidde.blog/custom-select-…
This would be such a great addition 🤩 hidde.blog/custom-select-…