Navigation

Select

<oblyx-select> is a labeled dropdown composed with <oblyx-option> children, one per row. Underneath its custom trigger it mirrors those children into a real, visually hidden native <select>, so the control participates in native HTML form submission and constraint validation with zero consumer JavaScript. There is deliberately no wrapper field element: label, help and error are plain attributes on oblyx-select itself.

Basic select

Canada United States Mexico
HTMLKotlin
<oblyx-select label="Country" placeholder="Select a country">
  <oblyx-option value="ca">Canada</oblyx-option>
  <oblyx-option value="us">United States</oblyx-option>
  <oblyx-option value="mx">Mexico</oblyx-option>
</oblyx-select>
oblyxSelect(label = "Country", placeholder = "Select a country") {
  oblyxOption(value = "ca") { +"Canada" }
  oblyxOption(value = "us") { +"United States" }
  oblyxOption(value = "mx") { +"Mexico" }
}

Options and selection

Each <oblyx-option> contributes a value and a label: its rendered content, unless an explicit label attribute overrides it. value on oblyx-select seeds which option starts selected: it must match an oblyx-option value to have any visible effect. After that first render the select owns its own selection, the same way a native <select> owns selectedIndex; it fires a bubbling change event on every choice a reader makes.

Select with a preselected value

Canada United States Mexico
HTMLKotlin
<oblyx-select 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-select>
oblyxSelect(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, typeahead and click, and rendered disabled on the hidden native mirror <option> too.

Select with a disabled option

1A 1B (occupied) 1C
HTMLKotlin
<oblyx-select label="Seat" placeholder="Select a seat">
  <oblyx-option value="1a">1A</oblyx-option>
  <oblyx-option value="1b" disabled>1B (occupied)</oblyx-option>
  <oblyx-option value="1c">1C</oblyx-option>
</oblyx-select>
oblyxSelect(label = "Seat", placeholder = "Select a seat") {
  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: the same idiom a plain native <select> uses for an <option value=""> placeholder or reset row. Selecting it behaves like selecting any other option: its own label shows in the trigger, and "" is what ends up in FormData. This is how to build a filter whose reset state is a real, selectable choice rather than something bolted on separately.

value on oblyx-select itself follows the same rule, and it is what makes the two "nothing selected" states distinguishable: leaving it unset is not the same as setting it to "". Unset (the default, with no pre-selected option found either) means nothing is selected at all, so the placeholder text shows. Set to "", as below, it means the value="" row is selected, exactly like any other value.

Select with a reset option

Submit to see "" in FormData, a real value, not a missing one.

Any category Books Electronics
HTMLKotlin
<form onsubmit="event.preventDefault(); const data = new FormData(this); this.querySelector('output').textContent = 'category = ' + JSON.stringify(data.get('category'));">
  <oblyx-select 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-select>
  <button type="submit">Apply</button>
  <output></output>
</form>
form {
  oblyxSelect(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, and other components with a comparable control height.

Select sizes

Option A Option A Option A
HTMLKotlin
<oblyx-select label="Small" size="sm">
  <oblyx-option value="a">Option A</oblyx-option>
</oblyx-select>
<oblyx-select label="Medium" size="md">
  <oblyx-option value="a">Option A</oblyx-option>
</oblyx-select>
<oblyx-select label="Large" size="lg">
  <oblyx-option value="a">Option A</oblyx-option>
</oblyx-select>
oblyxSelect(label = "Small", size = OblyxElementSize.SM) {
  oblyxOption(value = "a") { +"Option A" }
}
oblyxSelect(label = "Medium", size = OblyxElementSize.MD) {
  oblyxOption(value = "a") { +"Option A" }
}
oblyxSelect(label = "Large", size = OblyxElementSize.LG) {
  oblyxOption(value = "a") { +"Option A" }
}

Disabled

Forwarded to both the visible trigger button and the hidden native mirror <select>.

Disabled select

United States
HTMLKotlin
<oblyx-select label="Country" value="us" disabled>
  <oblyx-option value="us">United States</oblyx-option>
</oblyx-select>
oblyxSelect(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 trigger; non-empty text is also rendered. This is purely informational and does not call setCustomValidity.

Select with error

Canada United States
HTMLKotlin
<oblyx-select label="Country" placeholder="Select a country" error="Choose a country to continue.">
  <oblyx-option value="ca">Canada</oblyx-option>
  <oblyx-option value="us">United States</oblyx-option>
</oblyx-select>
oblyxSelect(label = "Country", placeholder = "Select a country", error = "Choose a country to continue.") {
  oblyxOption(value = "ca") { +"Canada" }
  oblyxOption(value = "us") { +"United States" }
}

Form submission and validation

name on oblyx-select 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 trigger. required forwards to that same native select, so the browser's own "please select an item" validation blocks submission and reports validity, unlike an <input type="hidden"> mirror, which the HTML spec bars from constraint validation entirely.

Select inside a form

Submit without choosing a country to see native validation block it.

Canada United States Mexico
HTMLKotlin
<form onsubmit="event.preventDefault(); const data = new FormData(this); this.querySelector('output').textContent = 'country = ' + data.get('country');">
  <oblyx-select label="Country" name="country" placeholder="Select a country" required>
    <oblyx-option value="ca">Canada</oblyx-option>
    <oblyx-option value="us">United States</oblyx-option>
    <oblyx-option value="mx">Mexico</oblyx-option>
  </oblyx-select>
  <button type="submit">Continue</button>
  <output></output>
</form>
form {
  oblyxSelect(label = "Country", name = "country", placeholder = "Select a country", required = true) {
    oblyxOption(value = "ca") { +"Canada" }
    oblyxOption(value = "us") { +"United States" }
    oblyxOption(value = "mx") { +"Mexico" }
  }
  button(type = ButtonType.submit) { +"Continue" }
}

Reference

<oblyx-select>

AttributeTypeDefaultDescription
labelstring | undefinedLabel text, always rendered and associated with the trigger via a real <label for>. This works even though the trigger is a <button>, not an <input>: <button> is a "labelable" element per the HTML spec, so the browser's own accessible-name computation resolves a <label for> pointing at it exactly like it would for oblyx-input. Required for an accessible name; omitting it is a defect and is flagged loudly.
helpstring | undefinedHelp text, rendered between the label and the control and wired into the trigger's aria-describedby.
errorstring | undefinedError message. Presence alone puts the control in its error visual state and sets aria-invalid="true" on the trigger; non-empty text is also rendered and wired into aria-describedby, mirroring oblyx-input's error contract exactly.
sizesmmdlgmdForm-control height.
namestring | undefinedForwarded verbatim to the native <select name>. This is what makes the value appear in FormData/receiveParameters().
valuestring | undefinedInitial 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, not the same "nothing" collapsed into one: undefined (the default, with no value attribute set and no pre-selected option found) means nothing is selected, so the placeholder shows. '' means the <oblyx-option value=""> row is selected, the same as any other value: its own label renders in the trigger, and '' is what gets submitted. A caller builds a "no selection" / reset choice out of that second state, exactly like a native <select> with an <option value=""> reset row.
placeholderstring | undefinedSelect…Shown in the trigger when nothing is selected.
requiredbooleanfalseForwarded to the native mirror <select required>, enforcing real constraint validation via a real <select> rather than a hidden input.
disabledbooleanfalseNative disabled state, forwarded to both the trigger button and the native mirror <select>.

Events

EventTypeDescription
changeEventFired when the selected value changes as a result of user interaction, mirroring a native select element's own change timing. Not fired when the value property is set by script.

<oblyx-option>

AttributeTypeDefaultDescription
valuestring | undefinedThe 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>.
labelstring | undefinedOptional 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.
disabledbooleanfalseForwarded 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.
selectedbooleanfalseDriven 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.
highlightedbooleanfalseDriven 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.