Select
A styled select built on the native customizable-select API, rendered as
<select is="root-select">.
Usage
<select is="root-select" data-label="Choose an option">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
Where the customizable-select API is supported, the component injects the
<button><selectedcontent></selectedcontent></button>
picker structure and styles the picker, options, checkmark and picker icon; options may contain rich
markup. Where it is not supported, it leaves a plain native <select> as the fallback.
The component also builds a [field-wrapper] span around the control - always, with or without
a label - and that span carries the field box; the <label> and the hint are its
siblings.
The arrow is the system's own chevron - the same shape root-tree-view draws on a branch row,
masked onto a span so it takes currentcolor and turns over when the picker opens. The user
agent's ::picker-icon is hidden either way.
The field's floor is Input's floor: 20ch, twenty characters of the field's own font, which is
what a native <input> starts at. It used to measure 4.5px wider than an Input's from
the same declaration - ch is twenty characters of the element's font, and the
wrapper had inherited the label's medium weight instead of stating the field's own.
Placeholder
A leading <option value="" disabled> reads as prompt text rather than a real
choice - shown in the field's placeholder color for as long as it's the current selection, and never
itself selectable once left, the same way disabled keeps any option out of reach.
<select is="root-select" data-label="Country">
<option value="" disabled selected>Choose a country…</option>
<option value="it">Italy</option>
<option value="fr">France</option>
</select>
Sizes
data-size
takes small and large; medium is the default and needs no value written. The
step moves the field's height and the chevron with it - the text stays at the base step at every size,
which is the floor under which iOS Safari zooms the viewport on focus.
<select is="root-select" data-size="small" data-label="Small">
<option>Option 1</option>
</select>
<select is="root-select" data-label="Medium">
<option>Option 1</option>
</select>
<select is="root-select" data-size="large" data-label="Large">
<option>Option 1</option>
</select>
Hint and error
data-hint
holds the description and data-error the error message: both are authored once, and
data-invalid decides which one shows. With no data-error
the browser's own validationMessage stands in. One node carries both, wired to the control
through aria-describedby.
<select is="root-select" data-label="Country" data-hint="Where you live" data-error="Pick one">
<option>Italy</option>
</select>
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
value |
string |
- | Selected value |
data-size |
'small' | 'medium' | 'large' |
'medium' |
Control size |
disabled |
boolean |
false |
Disables the select |
data-label |
string |
- | Label text; the component builds the <label> |
required |
boolean |
false |
Marks the field required and draws the asterisk |
data-hint |
string |
- | Description shown under the field |
data-error |
string |
- | Error message; falls back to validationMessage |
data-invalid |
'true' |
- | Shows the error instead of the description |
Events
Namespaced as <component>.<event>, bubbling and cancelable;
detail also carries the originating DOM event as originalEvent.
| Event | Detail | Description |
|---|---|---|
root-select.input |
{ value } |
Fired on input |
root-select.change |
{ value } |
Fired on change |
root-select.focus |
{ value } |
Fired on focus |
root-select.blur |
{ value } |
Fired on blur |
root-select.invalid |
{ value } |
Fired when the control fails constraint validation |
root-select.data-label.change |
{ value, oldValue } |
Fired when the label text changes |
Extends
HTMLSelectElement - use with <select is="root-select">.