Select A form field that picks one or more options from a list: a trigger that looks like an Input , a Popup , and a Listbox . For a few short options always in view, use a segmented control or a Radio group; to filter a list or table, use a Filter chip .
Installation bun add @harmo-ui/css @harmo-ui/react npm install @harmo-ui/css @harmo-ui/react pnpm add @harmo-ui/css @harmo-ui/react yarn add @harmo-ui/css @harmo-ui/react pnpm dlx harmo-ui add select yarn dlx harmo-ui add select Usage import { Option , Select } from "@harmo-ui/react" ;
< Select label = "Channel" name = "channel" defaultValue = "whatsapp" >
< Option value = "whatsapp" >WhatsApp</ Option >
< Option value = "telegram" >Telegram</ Option >
< Option value = "viber" >Viber</ Option >
</ Select > Examples Default A trigger that looks like an Input, a Popup, and a Listbox. The popup opens and closes without a script. Focus stays on the trigger: the Arrow keys open the list and move the highlight, and Enter or Space chooses.
< Select label = "Channel" name = "channel" defaultValue = "whatsapp" >
< Option value = "whatsapp" >WhatsApp</ Option >
< Option value = "telegram" >Telegram</ Option >
< Option value = "viber" >Viber</ Option >
</ Select > < div class = "hfield" >
< span class = "hlabel" id = "channel-label" >Channel</ span >
< button
class = "hselect"
id = "channel"
type = "button"
data-name = "channel"
role = "combobox"
aria-labelledby = "channel-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "channel-listbox"
popovertarget = "channel-popup"
style = "anchor-name: --channel"
>
< span class = "hselect-value" >WhatsApp</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "channel-popup" popover style = "position-anchor: --channel" >
< ul class = "hlistbox" id = "channel-listbox" role = "listbox" aria-labelledby = "channel-label" >
< li class = "hoption" id = "channel-whatsapp" role = "option" data-value = "whatsapp" aria-selected = "true" >
< span class = "hoption-label" >WhatsApp</ span >
< svg class = "hoption-check" viewBox = "5.845 5.87 14 14" aria-hidden = "true" focusable = "false" >
< path
d = "M11.4261 16.455C11.2261 16.455 11.0361 16.375 10.8961 16.235L8.06613 13.405C7.77613 13.115 7.77613 12.635 8.06613 12.345C8.35613 12.055 8.83613 12.055 9.12613 12.345L11.4261 14.645L16.5661 9.505C16.8561 9.215 17.3361 9.215 17.6261 9.505C17.9161 9.795 17.9161 10.275 17.6261 10.565L11.9561 16.235C11.8161 16.375 11.6261 16.455 11.4261 16.455Z"
fill = "currentColor"
/>
</ svg >
</ li >
< li class = "hoption" id = "channel-telegram" role = "option" data-value = "telegram" aria-selected = "false" >
< span class = "hoption-label" >Telegram</ span >
</ li >
< li class = "hoption" id = "channel-viber" role = "option" data-value = "viber" aria-selected = "false" >
< span class = "hoption-label" >Viber</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "channel" value = "whatsapp" />
</ div >
View full source
Placeholder data-placeholder on the value grays it while nothing is chosen. The trigger takes a Field's Optional marker and description like any control.
Fallback channel Optional
Used when a message can't be delivered on the main channel.
Select a channel < Select
label = "Fallback channel"
optional = "Optional"
description = "Used when a message can't be delivered on the main channel."
name = "fallback"
placeholder = "Select a channel"
>
< Option value = "email" >Email</ Option >
< Option value = "sms" >SMS</ Option >
</ Select > < div class = "hfield" >
< div class = "hfield-label-row" >
< span class = "hlabel" id = "fallback-label" >Fallback channel</ span >
< span class = "hfield-optional" id = "fallback-optional" >Optional</ span >
</ div >
< p class = "hdescription" id = "fallback-description" >Used when a message can't be delivered on the main channel.</ p >
< button
class = "hselect"
id = "fallback"
type = "button"
data-name = "fallback"
role = "combobox"
aria-labelledby = "fallback-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "fallback-listbox"
aria-describedby = "fallback-optional fallback-description"
popovertarget = "fallback-popup"
style = "anchor-name: --fallback"
>
< span class = "hselect-value" data-placeholder >Select a channel</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "fallback-popup" popover style = "position-anchor: --fallback" >
< ul class = "hlistbox" id = "fallback-listbox" role = "listbox" aria-labelledby = "fallback-label" >
< li class = "hoption" id = "fallback-email" role = "option" data-value = "email" aria-selected = "false" >
< span class = "hoption-label" >Email</ span >
</ li >
< li class = "hoption" id = "fallback-sms" role = "option" data-value = "sms" aria-selected = "false" >
< span class = "hoption-label" >SMS</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "fallback" value = "" />
</ div >
View full source
Multiple With aria-multiselectable="true" on the list, each option starts with a .hoption-checkbox, the value lists the chosen labels, and the popup stays open while you choose.
Message statuses Delivered, Read
< Select
multiple
label = "Message statuses"
name = "statuses"
placeholder = "Any status"
defaultValue = { [ "delivered" , "read" ] }
>
< Option value = "delivered" >Delivered</ Option >
< Option value = "read" >Read</ Option >
< Option value = "failed" >Failed</ Option >
< Option value = "pending" >Pending</ Option >
</ Select >
View full source
< div class = "hfield" >
< span class = "hlabel" id = "statuses-label" >Message statuses</ span >
< button
class = "hselect"
id = "statuses"
type = "button"
data-name = "statuses"
role = "combobox"
aria-labelledby = "statuses-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "statuses-listbox"
popovertarget = "statuses-popup"
style = "anchor-name: --statuses"
>
< span class = "hselect-value" >Delivered, Read</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "statuses-popup" popover style = "position-anchor: --statuses" >
< ul
class = "hlistbox"
id = "statuses-listbox"
role = "listbox"
aria-labelledby = "statuses-label"
aria-multiselectable = "true"
>
< li class = "hoption" id = "statuses-delivered" role = "option" data-value = "delivered" aria-selected = "true" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Delivered</ span >
</ li >
< li class = "hoption" id = "statuses-read" role = "option" data-value = "read" aria-selected = "true" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Read</ span >
</ li >
< li class = "hoption" id = "statuses-failed" role = "option" data-value = "failed" aria-selected = "false" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Failed</ span >
</ li >
< li class = "hoption" id = "statuses-pending" role = "option" data-value = "pending" aria-selected = "false" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Pending</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "statuses" value = "delivered" />
< input type = "hidden" name = "statuses" value = "read" />
</ div >
View full source
Long value In a narrow container, drawn dashed here, the value stays on one line and ends with an ellipsis.
Sender number +41 44 7654321 · Customer support for Switzerland
< Select label = "Sender number" name = "sender" defaultValue = "support" >
< Option value = "support" >
+41 44 7654321 · Customer support for Switzerland
</ Option >
< Option value = "sales" >+44 20 7946 0000 · Sales</ Option >
</ Select > < div class = "hfield" >
< span class = "hlabel" id = "sender-label" >Sender number</ span >
< button
class = "hselect"
id = "sender"
type = "button"
data-name = "sender"
role = "combobox"
aria-labelledby = "sender-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "sender-listbox"
popovertarget = "sender-popup"
style = "anchor-name: --sender"
>
< span class = "hselect-value" >+41 44 7654321 · Customer support for Switzerland</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "sender-popup" popover style = "position-anchor: --sender" >
< ul class = "hlistbox" id = "sender-listbox" role = "listbox" aria-labelledby = "sender-label" >
< li class = "hoption" id = "sender-support" role = "option" data-value = "support" aria-selected = "true" >
< span class = "hoption-label" >+41 44 7654321 · Customer support for Switzerland</ span >
< svg class = "hoption-check" viewBox = "5.845 5.87 14 14" aria-hidden = "true" focusable = "false" >
< path
d = "M11.4261 16.455C11.2261 16.455 11.0361 16.375 10.8961 16.235L8.06613 13.405C7.77613 13.115 7.77613 12.635 8.06613 12.345C8.35613 12.055 8.83613 12.055 9.12613 12.345L11.4261 14.645L16.5661 9.505C16.8561 9.215 17.3361 9.215 17.6261 9.505C17.9161 9.795 17.9161 10.275 17.6261 10.565L11.9561 16.235C11.8161 16.375 11.6261 16.455 11.4261 16.455Z"
fill = "currentColor"
/>
</ svg >
</ li >
< li class = "hoption" id = "sender-sales" role = "option" data-value = "sales" aria-selected = "false" >
< span class = "hoption-label" >+44 20 7946 0000 · Sales</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "sender" value = "support" />
</ div >
View full source
Invalid aria-invalid="true" turns the border red, as for an Input.
Template language Select a language Choose a language for the template.
< Select
label = "Template language"
name = "language"
placeholder = "Select a language"
error = "Choose a language for the template."
>
< Option value = "en" >English</ Option >
< Option value = "es" >Spanish</ Option >
< Option value = "ar" >Arabic</ Option >
</ Select > < div class = "hfield" >
< span class = "hlabel" id = "language-label" >Template language</ span >
< button
class = "hselect"
id = "language"
type = "button"
data-name = "language"
role = "combobox"
aria-labelledby = "language-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "language-listbox"
aria-describedby = "language-error"
aria-invalid = "true"
popovertarget = "language-popup"
style = "anchor-name: --language"
>
< span class = "hselect-value" data-placeholder >Select a language</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "language-popup" popover style = "position-anchor: --language" >
< ul class = "hlistbox" id = "language-listbox" role = "listbox" aria-labelledby = "language-label" >
< li class = "hoption" id = "language-en" role = "option" data-value = "en" aria-selected = "false" >
< span class = "hoption-label" >English</ span >
</ li >
< li class = "hoption" id = "language-es" role = "option" data-value = "es" aria-selected = "false" >
< span class = "hoption-label" >Spanish</ span >
</ li >
< li class = "hoption" id = "language-ar" role = "option" data-value = "ar" aria-selected = "false" >
< span class = "hoption-label" >Arabic</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "language" value = "" />
< p class = "herror" id = "language-error" >Choose a language for the template.</ p >
</ div >
View full source
Disabled disabled grays the trigger and its chevron, as for an Input. Disable its hidden input too, so the form doesn't send it, as with a disabled native select.
< Select label = "Region" name = "region" defaultValue = "europe" disabled >
< Option value = "europe" >Europe</ Option >
</ Select > < div class = "hfield" >
< span class = "hlabel" id = "region-label" >Region</ span >
< button
class = "hselect"
id = "region"
type = "button"
data-name = "region"
role = "combobox"
aria-labelledby = "region-label"
aria-haspopup = "listbox"
aria-expanded = "false"
disabled
>
< span class = "hselect-value" >Europe</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< input type = "hidden" name = "region" value = "europe" disabled />
</ div >
View full source
Disabling Disabling the trigger disables its hidden inputs too, so a form leaves the value out, as it would a native select's. Here a checkbox turns the select off and on, as an app's settings might.
const [ reminding , setReminding ] = useState ( true );
return (
<>
< Field orientation = "horizontal" label = "Send a reminder" >
< Checkbox
name = "reminder"
checked = { reminding }
onChange = { ( event ) => setReminding ( event . target .checked) }
/>
</ Field >
< Select
label = "Reminder channel"
name = "reminder-channel"
defaultValue = "sms"
disabled = { ! reminding }
>
< Option value = "sms" >SMS</ Option >
< Option value = "email" >Email</ Option >
</ Select >
</>
);
View full source
< div class = "hfield" data-orientation = "horizontal" >
< input class = "hcheckbox" type = "checkbox" id = "reminder" name = "reminder" checked />
< label class = "hlabel" for = "reminder" >Send a reminder</ label >
</ div >
< div class = "hfield" >
< span class = "hlabel" id = "reminder-channel-label" >Reminder channel</ span >
< button
class = "hselect"
id = "reminder-channel"
type = "button"
data-name = "reminder-channel"
role = "combobox"
aria-labelledby = "reminder-channel-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "reminder-channel-listbox"
popovertarget = "reminder-channel-popup"
style = "anchor-name: --reminder-channel"
>
< span class = "hselect-value" >SMS</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "reminder-channel-popup" popover style = "position-anchor: --reminder-channel" >
< ul class = "hlistbox" id = "reminder-channel-listbox" role = "listbox" aria-labelledby = "reminder-channel-label" >
< li class = "hoption" id = "reminder-channel-sms" role = "option" data-value = "sms" aria-selected = "true" >
< span class = "hoption-label" >SMS</ span >
< svg class = "hoption-check" viewBox = "5.845 5.87 14 14" aria-hidden = "true" focusable = "false" >
< path
d = "M11.4261 16.455C11.2261 16.455 11.0361 16.375 10.8961 16.235L8.06613 13.405C7.77613 13.115 7.77613 12.635 8.06613 12.345C8.35613 12.055 8.83613 12.055 9.12613 12.345L11.4261 14.645L16.5661 9.505C16.8561 9.215 17.3361 9.215 17.6261 9.505C17.9161 9.795 17.9161 10.275 17.6261 10.565L11.9561 16.235C11.8161 16.375 11.6261 16.455 11.4261 16.455Z"
fill = "currentColor"
/>
</ svg >
</ li >
< li class = "hoption" id = "reminder-channel-email" role = "option" data-value = "email" aria-selected = "false" >
< span class = "hoption-label" >Email</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "reminder-channel" value = "sms" />
</ div >
View full source
The trigger is a button, so it submits nothing itself. Its data-name names a hidden input for each chosen option's data-value, which the script keeps in step. Reset restores the first choices. Here, Save shows what the form would send.
const [ sent , setSent ] = useState ( "Save to see what the form sends." );
const save = ( event : FormEvent < HTMLFormElement >) => {
event . preventDefault ();
const data = new URLSearchParams (
[ ...new FormData ( event .currentTarget)] . map (([ key , value ]) => [
key ,
String (value) ,
]) ,
) . toString ();
setSent (data ? `Sends ${ data } ` : "Sends nothing" );
};
return (
< form
style = { { display : "flex" , flexDirection : "column" , gap : 16 } }
onSubmit = { save }
>
< Select label = "Channel" name = "channel" defaultValue = "whatsapp" >
< Option value = "whatsapp" >WhatsApp</ Option >
< Option value = "telegram" >Telegram</ Option >
< Option value = "viber" >Viber</ Option >
</ Select >
< Select
multiple
label = "Notify me about"
name = "statuses"
placeholder = "No statuses"
defaultValue = { [ "failed" ] }
>
< Option value = "delivered" >Delivered</ Option >
< Option value = "read" >Read</ Option >
< Option value = "failed" >Failed</ Option >
</ Select >
< div style = { { display : "flex" , gap : 8 } } >
< Button type = "submit" >Save</ Button >
< Button variant = "secondary" type = "reset" >
Reset
</ Button >
</ div >
< output className = "hdescription" > { sent } </ output >
</ form >
);
View full source
< form style = "display: flex; flex-direction: column; gap: 16px" >
< div class = "hfield" >
< span class = "hlabel" id = "form-channel-label" >Channel</ span >
< button
class = "hselect"
id = "form-channel"
type = "button"
data-name = "channel"
role = "combobox"
aria-labelledby = "form-channel-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "form-channel-listbox"
popovertarget = "form-channel-popup"
style = "anchor-name: --form-channel"
>
< span class = "hselect-value" >WhatsApp</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "form-channel-popup" popover style = "position-anchor: --form-channel" >
< ul class = "hlistbox" id = "form-channel-listbox" role = "listbox" aria-labelledby = "form-channel-label" >
< li class = "hoption" id = "form-channel-whatsapp" role = "option" data-value = "whatsapp" aria-selected = "true" >
< span class = "hoption-label" >WhatsApp</ span >
< svg class = "hoption-check" viewBox = "5.845 5.87 14 14" aria-hidden = "true" focusable = "false" >
< path
d = "M11.4261 16.455C11.2261 16.455 11.0361 16.375 10.8961 16.235L8.06613 13.405C7.77613 13.115 7.77613 12.635 8.06613 12.345C8.35613 12.055 8.83613 12.055 9.12613 12.345L11.4261 14.645L16.5661 9.505C16.8561 9.215 17.3361 9.215 17.6261 9.505C17.9161 9.795 17.9161 10.275 17.6261 10.565L11.9561 16.235C11.8161 16.375 11.6261 16.455 11.4261 16.455Z"
fill = "currentColor"
/>
</ svg >
</ li >
< li class = "hoption" id = "form-channel-telegram" role = "option" data-value = "telegram" aria-selected = "false" >
< span class = "hoption-label" >Telegram</ span >
</ li >
< li class = "hoption" id = "form-channel-viber" role = "option" data-value = "viber" aria-selected = "false" >
< span class = "hoption-label" >Viber</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "channel" value = "whatsapp" />
</ div >
< div class = "hfield" >
< span class = "hlabel" id = "form-statuses-label" >Notify me about</ span >
< button
class = "hselect"
id = "form-statuses"
type = "button"
data-name = "statuses"
role = "combobox"
aria-labelledby = "form-statuses-label"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "form-statuses-listbox"
popovertarget = "form-statuses-popup"
style = "anchor-name: --form-statuses"
>
< span class = "hselect-value" >Failed</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "form-statuses-popup" popover style = "position-anchor: --form-statuses" >
< ul
class = "hlistbox"
id = "form-statuses-listbox"
role = "listbox"
aria-labelledby = "form-statuses-label"
aria-multiselectable = "true"
>
< li class = "hoption" id = "form-statuses-delivered" role = "option" data-value = "delivered" aria-selected = "false" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Delivered</ span >
</ li >
< li class = "hoption" id = "form-statuses-read" role = "option" data-value = "read" aria-selected = "false" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Read</ span >
</ li >
< li class = "hoption" id = "form-statuses-failed" role = "option" data-value = "failed" aria-selected = "true" >
< span class = "hoption-checkbox" aria-hidden = "true" ></ span >
< span class = "hoption-label" >Failed</ span >
</ li >
</ ul >
</ div >
< input type = "hidden" name = "statuses" value = "failed" />
</ div >
< div style = "display: flex; gap: 8px" >
< button class = "hbutton" type = "submit" >
< span class = "hbutton-label" >Save</ span >
</ button >
< button class = "hbutton" data-variant = "secondary" type = "reset" >
< span class = "hbutton-label" >Reset</ span >
</ button >
</ div >
< output class = "hdescription" >Save to see what the form sends.</ output >
</ form >
View full source
A select and an input share the same box, so they line up in a row. The row here is a plain flex container of your own.
< div style = { { display : "flex" , gap : 8 } } >
< Input
type = "search"
aria-label = "Search messages"
placeholder = "Search messages"
/>
< Select aria-label = "Status" placeholder = "Any status" >
< Option value = "delivered" >Delivered</ Option >
< Option value = "failed" >Failed</ Option >
</ Select >
</ div >
View full source
< div style = "display: flex; gap: 8px" >
< input class = "hinput" type = "search" aria-label = "Search messages" placeholder = "Search messages" />
< button
class = "hselect"
type = "button"
role = "combobox"
aria-label = "Status"
aria-haspopup = "listbox"
aria-expanded = "false"
aria-controls = "status-listbox"
popovertarget = "status-popup"
style = "anchor-name: --status"
>
< span class = "hselect-value" data-placeholder >Any status</ span >
< svg class = "hexpand-chevron" viewBox = "0 0 16 12" aria-hidden = "true" focusable = "false" >
< g class = "hexpand-chevron-head" >
< line class = "hexpand-chevron-left" x1 = "0" y1 = "6" x2 = "8" y2 = "6" />
< line class = "hexpand-chevron-right" x1 = "16" y1 = "6" x2 = "8" y2 = "6" />
</ g >
</ svg >
</ button >
< div class = "hpopup" id = "status-popup" popover style = "position-anchor: --status" >
< ul class = "hlistbox" id = "status-listbox" role = "listbox" aria-label = "Status" >
< li class = "hoption" id = "status-delivered" role = "option" aria-selected = "false" >
< span class = "hoption-label" >Delivered</ span >
</ li >
< li class = "hoption" id = "status-failed" role = "option" aria-selected = "false" >
< span class = "hoption-label" >Failed</ span >
</ li >
</ ul >
</ div >
</ div >
View full source
API reference React <Select> renders the trigger, its popup, and its list, and does everything a script would. With label, it renders the Field too; without one, name it with aria-label, or place it in a Setting , which names it. A disabled Select has no popup. Its children are <Option>s and <OptionGroup>s, as in a Listbox . Other props are the trigger <button>'s attributes.
HTML A <button class="hselect" type="button" role="combobox">, followed by a .hpopup[popover] holding a .hlistbox. Load field.css, select.css, popup.css, listbox.css, and expand-chevron.css.
It needs a script for the keyboard, selection, the shown value, and the form value. The trigger is a button, so in a form it submits nothing: hidden inputs carry the value instead, as a native <select> would send it. See Select's full contract .
Accessibility <button role="combobox"> gets native focus and disabled, and aria-labelledby names it from the Field's label.Focus stays on the trigger while the list is open (the WAI-ARIA select-only combobox pattern); the trigger's aria-activedescendant names the highlighted option. Keys: Down and Up move the highlight, Home and End go to the first and last option, typing jumps to the option whose label starts with the typed letters, Enter or Space chooses, Escape closes. Disabled options are skipped.