Skip to content

Technical review: Document customizable select listboxes#43352

Open
chrisdavidmills wants to merge 5 commits intomdn:mainfrom
chrisdavidmills:customizable-select-listbox
Open

Technical review: Document customizable select listboxes#43352
chrisdavidmills wants to merge 5 commits intomdn:mainfrom
chrisdavidmills:customizable-select-listbox

Conversation

@chrisdavidmills
Copy link
Contributor

@chrisdavidmills chrisdavidmills commented Mar 6, 2026

Description

Chrome has supported Customizable <select> elements (via appearance: base-select) for single dropdown selects since version 135, and since 145, now supports it for listbox selects, e.g. <select size="3"> or <select multiple>.

See https://chromestatus.com/feature/6222145025867776.

This PR does a few things:

  1. Adds a separate guide covering customizable select listboxes. I felt that the existing customizable select elements guide was long enough already, plus the listbox variant has some differences in intent and implementation that are worth calling out separately.
    • This one contains a few examples that could do with checking for accessibility, UX, suitability, etc. I ran with the idea of creating some complex/unusual listbox examples to show what is now possible, and I am hoping I didn't get too carried away.
  2. Updates the existing customizable select elements guide to correct the information about listboxes not being supported, which is now wrong, and adds decent coverage of using <optgroup> in customizable selects (this was previously glossed over).
  3. Updates nav menus to fit the new guide into the forms module sequence.
  4. Updates a few other articles to fix/add mentions as required.

Motivation

Additional details

Related issues and pull requests

Fixes #43339

@chrisdavidmills chrisdavidmills requested review from a team as code owners March 6, 2026 11:49
@chrisdavidmills chrisdavidmills requested review from hamishwillee and pepelsbey and removed request for a team March 6, 2026 11:49
@github-actions github-actions bot added Content:CSS Cascading Style Sheets docs Content:HTML Hypertext Markup Language docs Content:Learn Learning area docs labels Mar 6, 2026
@chrisdavidmills chrisdavidmills changed the title Document customizable select listboxes Technical review: Document customizable select listboxes Mar 6, 2026
@github-actions github-actions bot added the size/l [PR only] 501-1000 LoC changed label Mar 6, 2026
@josepharhar
Copy link

josepharhar commented Mar 7, 2026

I'm glad you built an example which does filtering, because I'm working on new primitives to help with that: https://open-ui.org/components/combobox.explainer/#associating-an-input-element-with-a-select-element

One issue with the example is that it puts the input inside the select element, if I read it correctly - this goes against the content model and has parsing issues, so you should put it outside of the select before it instead.

It would also be good to plan ahead for <select multiple size=1>, which i already shipped in chrome but im waiting for the spec to get approved - the size=1 opts you into a dropdown box for multi select. i am working on shipping base appearance for this mode too.

Was it explained anywhere that any size value greater than one opts you into listbox mode?

> [!NOTE]
> This article covers the background behind customizable selects and shows how to build "single dropdown" selects that take advantage of these features — that is, dropdown menus that display a single option at a time and allow a single option to be selected, created by `<select>`.
>
> For information on creating "listbox" selects — menus that display multiple options at once and allow a single option or multiple options to be selected, created by `<select multiple>` or `<select size="3">`, see [Customizable select listboxes](/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select_listboxes).
Copy link
Collaborator

@hamishwillee hamishwillee Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't — have a matching — ? I mean can you use comma as the end of the clause? Note, I'd also suggest here you don't need the "created by" bit.

Suggested change
> For information on creating "listbox" selects — menus that display multiple options at once and allow a single option or multiple options to be selected, created by `<select multiple>` or `<select size="3">`, see [Customizable select listboxes](/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select_listboxes).
> For information on creating "listbox" selects — menus that display multiple options at once and allow a single option or multiple options to be selected see [Customizable select listboxes](/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select_listboxes).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, yes — updated!

BTW, I'm going to implement these changes myself rather than accept the commits in the UI because I've already made other changes to the page, and I'm worried about merge conflicts. Sorry about that.

## Styling with CSS

The `<select>` element has historically been notoriously difficult to style productively with CSS, hence features being introduced to enable creating [fully customizable select elements](/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select).
The `<select>` element has historically been notoriously difficult to style productively with CSS, hence features being introduced to enable creating fully customizable select elements. See the following guides for more information:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `<select>` element has historically been notoriously difficult to style productively with CSS, hence features being introduced to enable creating fully customizable select elements. See the following guides for more information:
The `<select>` element has historically been difficult to style effectively with CSS.
The following guides have information about newer features that have been introduced to enable fully customizable select elements:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated


## Dropdown selects versus listboxes

When we talk about "dropdown" `<select>` elements, we are talking about the controls rendered for HTML like `<select>`. These feature a select button that, when pressed, shows a dropdown picker from which you can select different options. "Listbox" select elements on the other hand are controls rendered for HTML like `<select multiple>` or `<select size="3">`. These feature a box that shows multiple options at once, from which you can select one or multiple options.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first sentence needs work. It reads like select is a select. I understand you're trying to differentiate a bare select with one that has attributes, but this doesn't work. The comparative example below works really well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, to:

When we talk about "dropdown" <select> elements, we are talking about controls featuring a select button that, when pressed, shows a dropdown picker from which you can select an option. These are specified using basic HTML such as <select>.

"Listbox" <select> elements on the other hand are controls featuring a box that shows multiple options at once, from which you can select one or multiple options. You opt into rendering a "listbox" select by specifying the multiple attribute (to allow multiple selections) and/or a size value of more than 1. For example, <select multiple> or <select size="3">.


{{EmbedLiveSample("select-comparison", "100%", "200px")}}

It is simpler to style a customizable listbox `<select>` than it is to style the dropdown variant:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
It is simpler to style a customizable listbox `<select>` than it is to style the dropdown variant:
A customizable listbox `<select>` is easier to style than the dropdown variant:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated


It is simpler to style a customizable listbox `<select>` than it is to style the dropdown variant:

- There is no dropdown picker, so you don't need to worry about styling it with the {{cssxref("::picker()", "::picker(select)")}} pseudo-element, its {{cssxref(":open")}} and closed states, etc.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
- There is no dropdown picker, so you don't need to worry about styling it with the {{cssxref("::picker()", "::picker(select)")}} pseudo-element, its {{cssxref(":open")}} and closed states, etc.
- There is no dropdown picker, so you don't need to worry about styling it with the {{cssxref("::picker()", "::picker(select)")}} pseudo-element, its {{cssxref(":open")}} and closed states, or the content of the picker in the closed state.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I see that this is covered in the next point. So what is "etc" here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. I've updated it to remove the etc. and just use "or" between the two.

- You don't need to worry about styling the select button's icon using {{cssxref("::picker-icon")}}, or manipulating how the currently selected `<option>` is displayed inside the button using the {{htmlelement("selectedcontent")}} element.
- There is only a single container involved; you don't need to worry about the position of the picker relative to the button.

One of the major advantages of customizable `<select>` listboxes over "classic" select listboxes is that you can include a much wider variety of child elements inside them, which means great flexibility in terms of design and functionality. See [A more complex listbox](#a_more_complex_listbox) for an idea of what's possible.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Structually the fact that this is here feels wrong because this section is a comparision of dropdown vs select style, and here you're talking about new vs classic when we haven't even introduced why we needed a new customizable select.

I think I'd push this just below the very top parapraph following the structure "this is what a customizable listbox is", "These were historically hard to style", then this text. The end might be "below you'll see a simple listobx, but you can see A more complex listbox for an idea of what's possible."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see your point. I've moved it up to the top. I deleted the link, because all of the examples in this article really help to show what's possible.


The example renders like so:

{{EmbedLiveSample("complex-listbox", "100%", "360px")}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not render on PR or locally.

Image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my last set of changes to the example, this now renders better now in Firefox, but it still doesn't quite render properly because Firefox doesn't support base-select and some of the related features (it currently only works fully in Chromium browsers).

To mitigate this issue, I've added support banners that show up in non-supporting browsers.


In this example, we present the listbox options horizontally rather than vertically.

The HTML is the same as the previous examples, except that we've included an extra wrapper `<div>` to allow us to set a `width` on the `<select>` and then a different `width` on the wrapper so that all the `<options>` can be kept on one line and scrolled when the `<select>` becomes too narrow to fit them all.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The HTML is the same as the previous examples, except that we've included an extra wrapper `<div>` to allow us to set a `width` on the `<select>` and then a different `width` on the wrapper so that all the `<options>` can be kept on one line and scrolled when the `<select>` becomes too narrow to fit them all.
The HTML is the same as the previous examples, except that we've included an extra wrapper `<div>` to allow us to set a `width` on the `<select>` and then a different `width` on the wrapper so that all the `<option>` elements can be kept on one line and scrolled when the `<select>` becomes too narrow to fit them all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

}
```

Next, we style the three child containers — the filter `<input>`, the `.options` `<div>` that will contain our `<options>`, and the `.edit` `<div>` containing the link. Most notable here is that we give the `.options` `<div>` a fixed `height` and an {{cssxref("overflow-y")}} value of `scroll` so that the contained `<option>` elements will scroll inside it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Next, we style the three child containers — the filter `<input>`, the `.options` `<div>` that will contain our `<options>`, and the `.edit` `<div>` containing the link. Most notable here is that we give the `.options` `<div>` a fixed `height` and an {{cssxref("overflow-y")}} value of `scroll` so that the contained `<option>` elements will scroll inside it.
Next, we style the three child containers — the filter `<input>`, the `.options` `<div>` that will contain our `<option>` elements, and the `.edit` `<div>` containing the link. Most notable here is that we give the `.options` `<div>` a fixed `height` and an {{cssxref("overflow-y")}} value of `scroll` so that the contained `<option>` elements will scroll inside it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been rewritten anyway, and the problem fixed.

}
```

Next, we give the `<option>` elements some extra padding to space them out horizontally, and a {{cssxref("position")}} value of relative so we can position their descendents relative to them.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preferred spelling I understand

Suggested change
Next, we give the `<option>` elements some extra padding to space them out horizontally, and a {{cssxref("position")}} value of relative so we can position their descendents relative to them.
Next, we give the `<option>` elements some extra padding to space them out horizontally, and a {{cssxref("position")}} value of relative so we can position their descendants relative to them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

</select>
```

We start our CSS by styling the `<optgroup>` elements themselves. These are mostly rudimentary styles to make the optgroups look like containers for their descendent `<option>` elements. We've given them some {{cssxref("margin-top")}} to put some space between each optgroup, and between the top optgroup and the select button.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
We start our CSS by styling the `<optgroup>` elements themselves. These are mostly rudimentary styles to make the optgroups look like containers for their descendent `<option>` elements. We've given them some {{cssxref("margin-top")}} to put some space between each optgroup, and between the top optgroup and the select button.
We start our CSS by styling the `<optgroup>` elements themselves. These are mostly rudimentary styles to make the optgroups look like containers for their descendant `<option>` elements. We've given them some {{cssxref("margin-top")}} to put some space between each optgroup, and between the top optgroup and the select button.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated ;-)

}
```

Most of the styling is fairly rudimentary, but we'll run throught it, pointing out anything significant along the way. The `<select>` element is given a `height` of `fit-content` so that it exactly fits its three child containers.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Most of the styling is fairly rudimentary, but we'll run throught it, pointing out anything significant along the way. The `<select>` element is given a `height` of `fit-content` so that it exactly fits its three child containers.
Most of the styling is fairly rudimentary, but we'll run through it, pointing out anything significant along the way. The `<select>` element is given a `height` of `fit-content` so that it exactly fits its three child containers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Collaborator

@hamishwillee hamishwillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Examples are clear and good. Definitely worth having a separate doc for this kind of select element.

My main concern is about the introductory structure of the new doc - see comments. Needs to provide a more clear picture of what this adds over the old way.

@chrisdavidmills
Copy link
Contributor Author

I'm glad you built an example which does filtering, because I'm working on new primitives to help with that: https://open-ui.org/components/combobox.explainer/#associating-an-input-element-with-a-select-element

@josepharhar this is really cool. I'm assuming that the declarative filter will just work? Coding the filter up is not that difficult, but it is still a fair chunk of JS.

One issue with the example is that it puts the input inside the select element, if I read it correctly - this goes against the content model and has parsing issues, so you should put it outside of the select before it instead.

Yeah, I should have thought about that a bit more carefully ;-)

I've updated the example on the page to put the input outside the select instead.

It would also be good to plan ahead for <select multiple size=1>, which i already shipped in chrome but im waiting for the spec to get approved - the size=1 opts you into a dropdown box for multi select. i am working on shipping base appearance for this mode too.

I don't want to publish information on this until it is in the spec and shipped, but we can at least start to prepare. Is there a write-up anywhere of the features particular to this and how they differ from regular customization dropdown selects?

Was it explained anywhere that any size value greater than one opts you into listbox mode?

I've added a note to the new guide. I'll also aim to add explanatory notes to the <select> page once we update that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:CSS Cascading Style Sheets docs Content:HTML Hypertext Markup Language docs Content:Learn Learning area docs size/l [PR only] 501-1000 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Outdated information on lack of support for multiple attribute

3 participants