Combobox
<oblyx-combobox> is a labeled dropdown, composed with <oblyx-option> children exactly like select, whose text input filters those options as the reader types. Typing narrows the list by a case-insensitive substring match against each option's label; picking one (by keyboard or pointer) commits it the same way oblyx-select does, mirroring the choice into a real, visually hidden native <select> so the control participates in native HTML form submission and constraint validation with zero consumer JavaScript. Typed text that matches nothing, or is abandoned without picking an option, never becomes the value. This is a filterable select, not a freeform text field.
Basic combobox
<oblyx-combobox label="Country" placeholder="Search countries…">
<oblyx-option value="ca">Canada</oblyx-option>
<oblyx-option value="us">United States</oblyx-option>
<oblyx-option value="mx">Mexico</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Country", placeholder = "Search countries…") {
oblyxOption(value = "ca") { +"Canada" }
oblyxOption(value = "us") { +"United States" }
oblyxOption(value = "mx") { +"Mexico" }
}Filtering a longer list
Try typing "an" or "berry": the popup narrows to matching rows as you type, and announces the result count for screen readers. Reopening the popup (arrow keys, or clicking the field again) always starts from the full list, even when a value is already selected.
Combobox with many options
<oblyx-combobox label="Fruit" placeholder="Search fruit…">
<oblyx-option value="apple">Apple</oblyx-option>
<oblyx-option value="apricot">Apricot</oblyx-option>
<oblyx-option value="banana">Banana</oblyx-option>
<oblyx-option value="blueberry">Blueberry</oblyx-option>
<oblyx-option value="cherry">Cherry</oblyx-option>
<oblyx-option value="cranberry">Cranberry</oblyx-option>
<oblyx-option value="grape">Grape</oblyx-option>
<oblyx-option value="mango">Mango</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Fruit", placeholder = "Search fruit…") {
oblyxOption(value = "apple") { +"Apple" }
oblyxOption(value = "apricot") { +"Apricot" }
oblyxOption(value = "banana") { +"Banana" }
oblyxOption(value = "blueberry") { +"Blueberry" }
oblyxOption(value = "cherry") { +"Cherry" }
oblyxOption(value = "cranberry") { +"Cranberry" }
oblyxOption(value = "grape") { +"Grape" }
oblyxOption(value = "mango") { +"Mango" }
}Options and selection
Each <oblyx-option> contributes a value and a label: its rendered content, unless an explicit label attribute overrides it, identical to oblyx-select. value on oblyx-combobox seeds which option starts selected; after that first render the combobox owns its own selection and fires a bubbling change event on every choice the reader commits.
Combobox with a preselected value
<oblyx-combobox label="Country" value="us">
<oblyx-option value="ca">Canada</oblyx-option>
<oblyx-option value="us">United States</oblyx-option>
<oblyx-option value="mx">Mexico</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Country", value = "us") {
oblyxOption(value = "ca") { +"Canada" }
oblyxOption(value = "us") { +"United States" }
oblyxOption(value = "mx") { +"Mexico" }
}An option can also be disabled. A disabled option is skipped by keyboard navigation and click, and rendered disabled on the hidden native mirror <option> too.
Combobox with a disabled option
<oblyx-combobox label="Seat" placeholder="Search seats…">
<oblyx-option value="1a">1A</oblyx-option>
<oblyx-option value="1b" disabled>1B (occupied)</oblyx-option>
<oblyx-option value="1c">1C</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Seat", placeholder = "Search seats…") {
oblyxOption(value = "1a") { +"1A" }
oblyxOption(value = "1b", disabled = true) { +"1B (occupied)" }
oblyxOption(value = "1c") { +"1C" }
}A reset ("Any") option
value="" on an <oblyx-option> is a real, selectable value, not a missing one, identical to select's own rule. Selecting it fills the input with its own label, the same as picking any other option, and "" is what ends up in FormData: a reset choice built the same way as any other row, not bolted on separately.
value on oblyx-combobox itself follows the same rule select's does: unset (the default) means nothing is selected, so the placeholder shows and the input starts empty; set to "", as below, the value="" row is selected and its label fills the input.
Combobox with a reset option
Submit to see "" in FormData, a real value, not a missing one.
<form onsubmit="event.preventDefault(); const data = new FormData(this); this.querySelector('output').textContent = 'category = ' + JSON.stringify(data.get('category'));">
<oblyx-combobox label="Category" name="category" value="">
<oblyx-option value="">Any category</oblyx-option>
<oblyx-option value="books">Books</oblyx-option>
<oblyx-option value="electronics">Electronics</oblyx-option>
</oblyx-combobox>
<button type="submit">Apply</button>
<output></output>
</form>form {
oblyxCombobox(label = "Category", name = "category", value = "") {
oblyxOption(value = "") { +"Any category" }
oblyxOption(value = "books") { +"Books" }
oblyxOption(value = "electronics") { +"Electronics" }
}
button(type = ButtonType.submit) { +"Apply" }
}Sizes
size takes OblyxElementSize, shared with button, input, select and other components with a comparable control height.
Combobox sizes
<oblyx-combobox label="Small" size="sm">
<oblyx-option value="a">Option A</oblyx-option>
</oblyx-combobox>
<oblyx-combobox label="Medium" size="md">
<oblyx-option value="a">Option A</oblyx-option>
</oblyx-combobox>
<oblyx-combobox label="Large" size="lg">
<oblyx-option value="a">Option A</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Small", size = OblyxElementSize.SM) {
oblyxOption(value = "a") { +"Option A" }
}
oblyxCombobox(label = "Medium", size = OblyxElementSize.MD) {
oblyxOption(value = "a") { +"Option A" }
}
oblyxCombobox(label = "Large", size = OblyxElementSize.LG) {
oblyxOption(value = "a") { +"Option A" }
}Disabled
Forwarded to both the visible text input and the hidden native mirror <select>.
Disabled combobox
<oblyx-combobox label="Country" value="us" disabled>
<oblyx-option value="us">United States</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Country", value = "us", disabled = true) {
oblyxOption(value = "us") { +"United States" }
}Error
Setting error puts the control in its error visual state and sets aria-invalid="true" on the input; non-empty text is also rendered. This is purely informational and does not call setCustomValidity.
Combobox with error
<oblyx-combobox label="Country" placeholder="Search countries…" error="Choose a country to continue.">
<oblyx-option value="ca">Canada</oblyx-option>
<oblyx-option value="us">United States</oblyx-option>
</oblyx-combobox>oblyxCombobox(label = "Country", placeholder = "Search countries…", error = "Choose a country to continue.") {
oblyxOption(value = "ca") { +"Canada" }
oblyxOption(value = "us") { +"United States" }
}Form submission and validation
name on oblyx-combobox is what makes the chosen option's value appear in FormData under that key: it is forwarded to the hidden native <select name>, not the visible text input. required forwards to that same native select, so the browser's own "please select an item" validation blocks submission and reports validity.
Combobox inside a form
Submit without choosing a country to see native validation block it.
<form onsubmit="event.preventDefault(); const data = new FormData(this); this.querySelector('output').textContent = 'country = ' + data.get('country');">
<oblyx-combobox label="Country" name="country" placeholder="Search countries…" required>
<oblyx-option value="ca">Canada</oblyx-option>
<oblyx-option value="us">United States</oblyx-option>
<oblyx-option value="mx">Mexico</oblyx-option>
</oblyx-combobox>
<button type="submit">Continue</button>
<output></output>
</form>form {
oblyxCombobox(label = "Country", name = "country", placeholder = "Search countries…", required = true) {
oblyxOption(value = "ca") { +"Canada" }
oblyxOption(value = "us") { +"United States" }
oblyxOption(value = "mx") { +"Mexico" }
}
button(type = ButtonType.submit) { +"Continue" }
}Keyboard support
↓/↑ open the popup (showing the full list) or move the highlight; Home/End jump to the first/last enabled visible row while open; Enter commits the highlighted row; Escape closes the popup and restores the input's text to the current selection, discarding anything typed. Typing itself is left entirely to the browser's normal text editing. Every other key just types, and the resulting filter is read from the input's own value.
Reference
<oblyx-combobox>
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | undefined | – | Label text, always rendered and associated with the input via a real <label for>. Required for an accessible name; omitting it is a defect and is flagged loudly. |
help | string | undefined | – | Help text, rendered between the label and the control and wired into the input's aria-describedby. |
error | string | undefined | – | Error message. Presence alone puts the control in its error visual state and sets aria-invalid="true" on the input; non-empty text is also rendered and wired into aria-describedby, mirroring oblyx-select's error contract exactly. |
size | smmdlg | md | Form-control height. |
name | string | undefined | – | Forwarded verbatim to the native <select name>. This is what makes the value appear in FormData/receiveParameters(). |
value | string | undefined | – | Initial selected value only. Seeds the first render rather than acting as a fully-controlled value. Must match an <oblyx-option value> to have any visible effect. undefined and '' are deliberately distinct states, identical to oblyx-select's own value contract: undefined means nothing is selected, so the placeholder shows and the input starts empty. '' means the <oblyx-option value=""> row is selected, like any other value: its own label fills the input, and '' is what gets submitted. |
placeholder | string | undefined | – | Shown in the input when nothing is selected and nothing is typed. |
required | boolean | false | Forwarded to the hidden native mirror <select required>, so the browser's own constraint validation runs against that real <select>. It is never set on the visible text input, which has no native required semantics of its own. |
disabled | boolean | false | Native disabled state, forwarded to the input and to the native mirror <select>. |
Events
| Event | Type | Description |
|---|---|---|
change | Event | Fired when the selected value changes as a result of the reader choosing an option, mirroring a native select element's own change timing. Not fired when the value property is set by script, and not fired by typing alone. |
<oblyx-option>
| Attribute | Type | Default | Description |
|---|---|---|---|
value | string | undefined | – | The value this option contributes to the select. Required, like a native <option value>. Missing it is a defect and is flagged loudly rather than silently omitting the row from <oblyx-select>'s model. An empty string is a legitimate value in its own right, not a missing one: the standard native idiom for a placeholder or reset choice, exactly like <option value=""> in a plain HTML <select>. |
label | string | undefined | – | Optional explicit label for the trigger's collapsed display and the hidden native <select> mirror's <option> text. Falls back to the rendered content, since that is what is visually true for a light-DOM row that may contain more than plain text. See displayLabel. |
disabled | boolean | false | Forwarded to the native mirror <option disabled> and to this row's own aria-disabled/click handling. A disabled option is skipped by keyboard navigation, typeahead and click. |
selected | boolean | false | Driven by the parent <oblyx-select>, true for the one row whose value matches the select's current value. Reflected so oblyx-option[selected] is a CSS hook, and so an author can pre-select a row with a plain selected attribute the same way a native <option selected> works. |
highlighted | boolean | false | Driven by the parent <oblyx-select>, true for the row the keyboard/mouse cursor currently rests on. Purely visual; aria-activedescendant on the trigger is the real accessibility signal, this is just its CSS-visible counterpart. |