Add explainer: slotted options in customizable <select>#1357
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new explainer proposing that customizable <select> (appearance: base-select) should treat <option>, <optgroup>, and the trigger <button> as effective children when they are provided via slotting (including across nested components), enabling reusable select-based web components without JS cloning/workarounds.
Changes:
- Introduces a new explainer documenting the current limitation with slotted
<option>elements and why it matters for accessibility. - Specifies proposed behavior for slotted options/optgroups/trigger button, including nested slot scenarios and JS API expectations.
- Documents alternative approaches (JS rebuild, MutationObserver cloning, customized built-ins, etc.) and A11y/i18n/privacy/security considerations.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
anaskim
left a comment
There was a problem hiding this comment.
Overall, this seems like a good start to me. I wonder if we need to mention the issues that we could encounter if we implement Joey's solution: "I would solve this by changing the select element to look for options in its flat tree." Just so that we can point folks to a section in this doc if asked.
| component; this proposal doesn't let a shadow tree reach content it couldn't | ||
| already compose. | ||
|
|
||
| ## Stakeholder Feedback / Opposition |
There was a problem hiding this comment.
Do we also want to include browser's positions here? (Since I assume Chromium's should be positive)
There was a problem hiding this comment.
I don't know if we should do that now, as far as I know we request positions after having an explainer so we can reference it in the proposals, right?
kbabbitt
left a comment
There was a problem hiding this comment.
Great to see this! A few comments, mostly editorial.
| > **Note on scope:** This applies only to `<select>` in customizable mode | ||
| > (`appearance: base-select`). A `<select>` with the default native appearance is | ||
| > unchanged. |
There was a problem hiding this comment.
Why is default appearance out of scope? Couldn't I put a default appearance <select> inside a shadow DOM?
There was a problem hiding this comment.
It would be nice to have custom and non-custom select behave the same way for this. The biggest concern would be web compat. We might need use counters to see if this is a feasible change to make.
Maybe this should be an open question rather than something that's definitively scoped out?
There was a problem hiding this comment.
Yes, I was worried about web compat. Definitely open to more discussion about it and include it if necessary. @chrisdholt what are your thoughts?
There was a problem hiding this comment.
I think we actually lose the progressive enhancement story completely if this doesn't work generally for the non-customizable select. We can't fallback where it's not supported yet, we'd get an error in the console and it wouldn't render.
There was a problem hiding this comment.
Thanks all for the comments. I agree, I also think it'd be nice to have default select work as well, but I also think we should have some data to know this isn't gonna break stuff before pushing it (which seems like it'd be fine, but better safe than sorry).
What about this path forward?
- Start with base-select. It's the motivating use case and it's low risk (base-select adoption is only ~0.0003% of page loads today and this change would only affect the subset of those that use slotted options).
- Measure web compat for extending to the default select in parallel (no counter exists for this yet).
- Decide the default select scope in the WHATWG thread The select element doesn't support slotted option elements whatwg/html#11535 with that data plus the progressive enhancement argument. We can raise it there for discussion as soon as this explainer is merged.
| > **Note on scope:** This applies only to `<select>` in customizable mode | ||
| > (`appearance: base-select`). A `<select>` with the default native appearance is | ||
| > unchanged. |
There was a problem hiding this comment.
It would be nice to have custom and non-custom select behave the same way for this. The biggest concern would be web compat. We might need use counters to see if this is a feasible change to make.
Maybe this should be an open question rather than something that's definitively scoped out?
Adds a new
SlottedOption/explainer proposing that a customizable<select>(appearance: base-select) recognize<option>,<optgroup>, and its trigger<button>when they are slotted in from a web component, so they behave as if they were direct children of the<select>.Today a
<select>only treats its direct DOM children as options. If you put a<select>in a component's shadow root and expose a<slot>, the slotted options are ignored and the dropdown is empty. The only options today are to sync optionswith a
MutationObserveror rebuild the control by hand, which is a lot of code and is usually less accessible than the native control.Discussion in WHATWG HTML issue: whatwg/html#11535