BwSelect| bw-select
Overview
BwSelect is a form control that allows the user to select one or more options from a list. It can be used as a single select or a multi select. Each item within the select should be a bw-option.
Usage
Basics
A select lets the user pick from a list of bw-options. Give it a label, an optional placeholder for the empty state, and a note for persistent helper text. Preselect an option with the value attribute (it must match an option's value).
<div class="flex-column gap">
<bw-select label="Country" placeholder="Select a country">
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
<bw-option value="mx">Mexico</bw-option>
</bw-select>
<bw-select label="Country (preselected)" value="ca" note="Defaults to Canada">
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
<bw-option value="mx">Mexico</bw-option>
</bw-select>
</div>
Reading the value
bw-select emits valueChange whenever the selection changes. Its detail is the selected value — a string for a single select, or an array for a multiple select. The event is scoped to the element (it does not bubble), so attach the listener directly to the bw-select.
<div class="flex-column gap">
<bw-select id="basicsSel" label="Favorite fruit" placeholder="Choose one">
<bw-option value="apple">Apple</bw-option>
<bw-option value="banana">Banana</bw-option>
<bw-option value="cherry">Cherry</bw-option>
</bw-select>
<bw-note id="basicsOut">valueChange: (nothing selected)</bw-note>
</div>
<script>
const select = document.querySelector('#basicsSel');
const out = document.querySelector('#basicsOut');
select.addEventListener('valueChange', e => {
out.textContent = 'valueChange: ' + e.detail;
});
</script>
Disabled
disabled prevents all interaction and removes the control from form submission. readonly keeps the value visible and submittable but blocks changes.
<div class="flex-column gap">
<bw-select label="Disabled" value="ca" disabled>
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
</bw-select>
<bw-select label="Read only" value="ca" readonly>
<bw-option value="us">United States</bw-option>
<bw-option value="ca">Canada</bw-option>
</bw-select>
</div>
Forms
bw-select is a form-associated control. Give it a name, place it in a bw-form (or native <form>), and its value is included on submit — a string for a single select, or a comma-joined string for a multiple select.
<bw-form id="roleForm" class="flex-column gap">
<bw-select name="role" label="Role" required placeholder="Select a role">
<bw-option value="admin">Admin</bw-option>
<bw-option value="editor">Editor</bw-option>
<bw-option value="viewer">Viewer</bw-option>
</bw-select>
<bw-button type="submit">Submit</bw-button>
<bw-note id="roleOut">Submit the form to see its data.</bw-note>
</bw-form>
<script>
const form = document.querySelector('#roleForm');
const out = document.querySelector('#roleOut');
form.addEventListener('dataSubmit', e => {
out.textContent = 'dataSubmit: ' + JSON.stringify(e.detail);
});
</script>
Required
A required select is invalid until something is selected, and adds an asterisk to its label. bw-form won't submit while it's empty. The error message appears once the user has interacted with the control.
<bw-form class="flex-column gap">
<bw-select name="plan" label="Plan" required placeholder="Choose a plan">
<bw-option value="free">Free</bw-option>
<bw-option value="pro">Pro</bw-option>
</bw-select>
<bw-button type="submit">Continue</bw-button>
</bw-form>
Custom validation
For rules the required attribute can't express, call the setCustomValidity(message) method — pass a message to mark the control invalid, or null to clear it. In this example only "Two" is accepted.
<bw-select id="cvSelect" label="Pick the number two" placeholder="Choose">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>
<script>
const select = document.querySelector('#cvSelect');
select.addEventListener('valueChange', e => {
select.setCustomValidity(e.detail === '2' ? null : 'Please choose Two');
});
</script>
Styling validity with CSS states
Like other form controls, bw-select exposes CSS custom states you can target: --required, --optional, --valid, --invalid, and — only after the user has interacted — --user-valid and --user-invalid.
<div>
<style>
@scope {
bw-select:state(--user-valid)::part(button) {
border: solid 1px var(--bw-green-500);
}
}
</style>
<bw-select label="Turns green once chosen" required placeholder="Choose one">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
</bw-select>
</div>
See the Forms tutorial for the full picture, including edit forms,
dataPatch, and how an invalid form blocks submission and focuses the first invalid control.
Icons
bw-select can show status indicators in the trigger. These are purely visual — they communicate state but have no effect on form validation.
pending— shows a loading spinner (e.g. while options are loading).success— shows a green check.error— shows a red error icon. Set it totruefor the icon alone, or to a string to show that message in a tooltip on hover.
<div class="flex-column gap">
<bw-select label="Loading options" placeholder="Loading…" pending>
<bw-option value="1">One</bw-option>
</bw-select>
<bw-select label="Confirmed" value="1" success>
<bw-option value="1">One</bw-option>
</bw-select>
<bw-select label="With error icon" placeholder="Choose…" error="true">
<bw-option value="1">One</bw-option>
</bw-select>
<bw-select label="With error tooltip" placeholder="Choose…" error="Please make a selection">
<bw-option value="1">One</bw-option>
</bw-select>
</div>
For validation errors tied to form constraints, rely on forms instead — the control renders its own error message automatically once the user has interacted with it.
Interface
By default the options open in a popover on wide screens and a bottom sheet on narrow ones (≤768px wide), which keeps the control comfortable to use on touch devices. Set the interface attribute to force one regardless of screen size.
<div class="flex-column gap">
<bw-select label="Popover" interface="popover" placeholder="Opens in a popover">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>
<bw-select label="Bottom sheet" interface="sheet" placeholder="Opens in a bottom sheet">
<bw-option value="1">One</bw-option>
<bw-option value="2">Two</bw-option>
<bw-option value="3">Three</bw-option>
</bw-select>
</div>
Multiple
Add the multiple attribute to let the user choose more than one option. The value becomes an array of strings, selected options appear as removable badges in the trigger, and a Select All / Deselect All shortcut is added to the list.
<bw-select multiple label="Toppings" placeholder="Choose toppings">
<bw-option value="cheese">Cheese</bw-option>
<bw-option value="pepperoni">Pepperoni</bw-option>
<bw-option value="mushroom">Mushroom</bw-option>
<bw-option value="onion">Onion</bw-option>
<bw-option value="pepper">Green pepper</bw-option>
</bw-select>
When every option is selected the trigger collapses to "All", and when there are more badges than fit, the overflow is summarized as "+N". Remove a selection by clicking the badge's close button.
Preselecting a multiple select
Because the value is an array, set it in JavaScript rather than as an attribute.
<bw-select id="multiPre" multiple label="Toppings">
<bw-option value="cheese">Cheese</bw-option>
<bw-option value="pepperoni">Pepperoni</bw-option>
<bw-option value="mushroom">Mushroom</bw-option>
<bw-option value="onion">Onion</bw-option>
</bw-select>
<script>
document.querySelector('#multiPre').value = ['cheese', 'mushroom'];
</script>
Options
Each item in a select is a bw-option. Its value is what the form submits; if you omit value, the option's text is used instead.
<bw-select label="Color" placeholder="Pick a color">
<bw-option>Red</bw-option>
<bw-option>Green</bw-option>
<bw-option>Blue</bw-option>
</bw-select>
Display value
Use display-value to show different text in the trigger than in the option list — handy when the list needs detail but the closed control should stay compact.
<bw-select label="Plan" value="pro" style="width: 400px;">
<bw-option value="free" display-value="Free">Free — for individuals</bw-option>
<bw-option value="pro" display-value="Pro">Pro — $9/month</bw-option>
<bw-option value="team" display-value="Team">Team — $20/user/month</bw-option>
</bw-select>
Disabled options
Mark an individual option disabled to keep it visible but unselectable.
<bw-select label="Plan" placeholder="Choose a plan" style="width: 400px;">
<bw-option value="free">Free</bw-option>
<bw-option value="pro">Pro</bw-option>
<bw-option value="enterprise" disabled>Enterprise (contact sales)</bw-option>
</bw-select>
Sizes
Use the size attribute to match the density of the surrounding UI. The default is small.
<div class="flex-column gap">
<bw-select size="small" value="1" label="Small">
<bw-option value="1">Small</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
<bw-select size="medium" value="1" label="Medium">
<bw-option value="1">Medium</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
<bw-select size="large" value="1" label="Large">
<bw-option value="1">Large</bw-option>
<bw-option value="2">Another</bw-option>
</bw-select>
</div>
Properties
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
disabled | disabled | Makes the input disabled and prevents changes | boolean | false |
error | error | Shows an error icon. If a string is passed in, it will render the icon as a tooltip. Has no effect on form validation | any | undefined |
interface | interface | The interface the control will be displayed on | "popover" | "sheet" | undefined |
label | label | Label that goes above the input | string | undefined |
multiple | multiple | Allows selecting multiple options and changes the value type to an array | boolean | false |
note | note | Informational text directly below the control | string | undefined |
originalValue | original-value | The default value the control will reset to in a form. If not set, will default to the inital value of the "value" property. | string | string[] | undefined |
pending | pending | Shows a loading indicator | boolean | false |
placeholder | placeholder | Placeholder for the input | string | undefined |
readonly | readonly | Makes the input readonly and prevents changes | boolean | false |
required | required | Marks it as required in a form and shows an asterisk at the end of the label | boolean | false |
size | size | Size of the control | "large" | "medium" | "small" | 'small' |
success | success | Shows a success icon. Has no effect on form validation | boolean | false |
value | value | The currently selected value of the controls. Will be a string aray if it's a multiselect | string | string[] | undefined |
Events
| Event | Description | Type |
|---|---|---|
ready | Emits once when the component has completed it's initial render | CustomEvent<any> |
valueChange | Emits the currently selected values whenever they change | CustomEvent<string | string[]> |
Methods
checkValidity() => Promise<boolean>
Checks the validity of the select
Returns
Type: Promise<boolean>
true if the select is valid, false otherwise
reportValidity() => Promise<boolean>
Reports the validity of the select
Returns
Type: Promise<boolean>
true if the select is valid, false otherwise
setCustomValidity(message: string | null) => Promise<void>
Sets the custom validity of the select
Parameters
| Name | Type | Description |
|---|---|---|
message | string | - The message to set the custom validity to |
Returns
Type: Promise<void>
Slots
| Slot | Description |
|---|---|
"end" | Content placed after selected value/s |
"label" | The label of the select |
"note" | The note of the select |
"start" | Content placed before selected value/s |
Shadow Parts
| Part | Description |
|---|---|
"button" | The button of the select |
"main" | The main div container of the select |
"popover" | The popover of the select |
"sheet" | The bottom sheet of the select |
CSS Custom Properties
| Name | Description |
|---|---|
--height | Height of the select |
--input-color | Color of the selected value/s in the main container |
--label-color | Color of the label |
--padding-inline | Padding inline of the select |
--placeholder-color | Placeholder color of the select |
Dependencies
Used by
Depends on
- bw-badge
- bw-label
- bw-popover
- bw-overlay
- bw-view
- bw-button
- bw-note
- bw-loading
- bw-color
- bw-icon
- bw-tooltip
Graph
© 2025 United Systems & Software - All Rights Reserved.