PicklistField
<caos-picklist-field>
a field choosing one value from a fixed list
May be placed on a record page, a page inside an app and a step of a guided flow.
Inputs
Section titled “Inputs”| Name | Attribute | Description | Type | Default | Required |
|---|---|---|---|---|---|
options |
property only | The selectable values, as an array of { value, label, qualifier?, image?, icon?, description?, amount? } or of plain strings. Setting it replaces the whole list and takes precedence over any declared <option> children. qualifier — a quieter second line under the label, what the option IS rather than merely its name — only ever shows once filterable is set; a native option has nowhere to draw a second line. image, icon, description and amount are what an option is drawn FROM in the cards presentation (see presentation), each optional and each simply not drawn when absent; outside that presentation they are carried and unshown, except that a description stands in for a missing qualifier. |
json | none declared | Optional |
filterable |
filterable, also a property |
Draw a typeahead over a listbox instead of the native select. Required for a qualifier to actually show, and worth reaching for on its own once the set is long enough that typing beats scanning. Typing filters by substring against BOTH an option’s label and its qualifier, case-insensitively. The native select stays underneath, hidden, so name still forwards a value into a submitted form exactly as before. |
boolean | false | Optional |
presentation |
presentation, markup only |
How the same declared list is drawn: dropdown (the default); cards, a row of selectable cards showing whichever of an option’s image, icon, description and amount it carries; or radio, plain radio buttons with every option visible. Each is a presentation of one list rather than a different field, so value, name, required, disabled, invalid and message all mean what they always did and the native select still submits. multiple wins over all of them — several values is the checkbox group — and cards and radio each displace filterable. Anything unrecognised reads as dropdown, so a presentation nothing can draw degrades to the list rather than to an empty field. Which one a real field uses is declared by the value set its options come from. The radio buttons are the platform’s own, so arrow-key movement, the single tab stop and the checked semantics are too. |
text — one of dropdown, cards, radio | dropdown | Optional |
multiple |
multiple, also a property |
Choose several values instead of one: the same options drawn as a group of checkboxes named by the label, and what the field holds becomes values. required, disabled, invalid, message and name apply to the group; filterable, placeholder and value do nothing. Once somebody has used a required group and left nothing chosen, it shows as invalid on its own. |
boolean | false | Optional |
values |
property only | What is chosen when multiple is set, as a list of option values, written back on every change in the order the options list them. A value not among the options is kept, after the rest, and not drawn — so a value since retired from the list is not erased by the next save. |
json | none declared | Optional |
dependsOn |
depends-on, also a property |
The field this dropdown waits on — declarative only, naming the relationship as metadata rather than coding it into a screen. Pair it with dependsOnValue, which the host keeps in sync with that field’s actual value; this input by itself does not fetch or filter anything. |
fieldRef | none declared | Optional |
dependsOnValue |
depends-on-value, also a property |
The CURRENT value of the field named by dependsOn, written by the host every time it changes — the same “data in” shape value already is. While dependsOn is set and this is empty, the field renders with NO real options — its placeholder still showing, still enabled and focusable — because there is nothing yet to choose from; that is the whole of the waiting state, and it needs no other input to say so. Becoming non-empty calls optionsSource, if one is set, with the new value. |
text | none declared | Optional |
optionsSource |
property only | The injected result source for a dependent dropdown — `(dependsOnValue) => options | Promise, in the same "data in, not queried" shape as CaosLookupField.search. Called once when dependsOnValuefirst becomes non-empty, and again every time it changes to a different value; whatever it returns becomes the option list. Optional — a host may instead set.options` itself in response to the same change, and the waiting state above still applies without this. |
json | none declared |
label |
label, markup only |
The field’s name, drawn above the control and pointed at the select, so clicking it focuses the control. It is also used as the accessible name. | text | none declared | Optional |
placeholder |
placeholder, markup only |
The “nothing chosen yet” line. Prepends a hidden, disabled, empty-valued first option and leaves it selected, drawn muted, until a real value is picked. Without it the first option is selected from the start. | text | none declared | Optional |
value |
value, also a property |
The chosen option’s value, not its label. It is written back on every change, so reading the attribute is reading the current selection. |
text | none declared | Optional |
name |
name, markup only |
The form name forwarded to the inner select, for a field submitted as part of a form. | text | none declared | Optional |
required |
required, markup only |
A value has to be chosen. Draws the asterisk beside the label and marks the select required. | boolean | false | Optional |
disabled |
disabled, markup only |
You may not change the selection. Greys the control and refuses focus. | boolean | false | Optional |
invalid |
invalid, markup only |
The selection is wrong — usually nothing chosen on a required field. Turns the outline and the message red. | boolean | false | Optional |
message |
message, markup only |
The line under the control — muted helper text on its own, the red reason when invalid is also set. |
text | none declared | Optional |
inline |
inline, markup only |
Draw the record-detail variant instead of the boxed form control: no box, an accent underline, inheriting the surrounding type, with the chevron pulled in tight. The label and the message are hidden. | boolean | false | Optional |
Events
Section titled “Events”| Name | When it fires | What it carries |
|---|---|---|
change |
When a different option is chosen — or, with multiple, when any box is ticked or cleared — after value or values has been written back. |
nothing |
Nothing goes inside this piece.
Styling hooks
Section titled “Styling hooks”| Design token | What it controls |
|---|---|
--caos-font |
The typeface of the label, the control and the message. |
--caos-text-body |
The size of the label and of the chosen value. |
--caos-space-1 |
The gap between the label, the control and the message. |
--caos-space-3 |
The vertical padding inside the control, and the chevron inset. |
--caos-space-5 |
With –caos-space-2, the room kept on the right so the value never runs under the chevron. |
--caos-space-2 |
Part of that same right-hand inset. |
--caos-radius |
The corner radius of the control. |
--caos-color-border |
The outline at rest. |
--caos-color-border-strong |
The outline while the control has focus. |
--caos-color-surface-2 |
The fill of the control. |
--caos-color-text |
The colour of the chosen value. |
--caos-color-text-muted |
The label, the unchosen placeholder, the chevron, the message and disabled text. |
--caos-color-accent |
The hover outline, and the underline of the inline variant. |
--caos-accent-wash |
The tint that fills the control while it has focus, in place of a focus ring. |
--caos-color-danger |
The required asterisk, and the outline and message when invalid. |
--caos-state-selected-bg |
The fill of the chosen card, and of the chosen row in the filterable list. |
Methods
Section titled “Methods”| Name | Description | Arguments |
|---|---|---|
focus() |
Move keyboard focus to the select, so typing a letter jumps to an option. The record-detail editor calls it on click-to-edit. | options: FocusOptions |
Readable state
Section titled “Readable state”Nothing on this piece can be read back.
Styling parts
Section titled “Styling parts”| Part | Which piece of it | When it is there |
|---|---|---|
::part(label) |
the label above the control | Always |
::part(select) |
the select element | Always |
::part(message) |
the line under the control | Always |
::part(combo) |
the typeahead box, once filterable is set |
Always |
::part(input) |
the text input inside the typeahead | Always |
::part(listbox) |
the filtered results list, once filterable is set |
Always |
::part(option) |
one result in that list | Always |
::part(group) |
the group of checkboxes, once multiple is set |
Always |
::part(radios) |
the group of radio buttons, in the radio presentation only | Always |
::part(radio) |
one option in the radio presentation — the label wrapping its button and its text | Always |
::part(cards) |
the row of cards, once presentation is cards |
Always |
::part(card) |
one card in that row | Always |
When to use it
Section titled “When to use it”One value out of a set that is already known — handed over whole, whether that is a short fixed list read without typing or a longer one (optionally depending on another field) searched with filterable. The line that never moves is “known set, handed over” versus “queried per keystroke against live records” — the moment it is the latter, reach for one of the pieces below instead.
What to use instead
Section titled “What to use instead”- LookupField — The value points at another RECORD, so the options have to be searched live rather than handed over as a known set — even a
filterablepicklist is still given its whole answer at once. - FieldRenderer — The type is not known until the record is read — hand it the field metadata and it draws the right control, and edits a multi-select as a group of checkboxes.
- OrderedPicker — Several values are chosen and their ORDER is part of the answer — columns left to right, entries top to bottom.
- CheckboxField — There are exactly two answers and they are yes and no.
- SegmentedFilter — The choice changes what is shown rather than recording a value on a record.
- OverflowMenu — The list is a set of actions to perform rather than a value to store.
Accessibility
Section titled “Accessibility”By default the control is a real <select> with a generated id and a label pointing at it, so it is announced with its name, clicking the label focuses it, and the whole native picker comes with it: keyboard opening, arrow keys, type-ahead by first letter, and the platform list on a touch device. Setting filterable swaps it for a real role="combobox" (aria-expanded, aria-controls, aria-autocomplete="list") over a role="listbox" of options, with an aria-activedescendant the arrow keys move rather than DOM focus — the same pattern LookupField uses for the same reason. required and invalid are forwarded as required/aria-invalid in both renders, and the label is also set as aria-label. A dependency that is not yet satisfied never disables the control — it stays focusable and its placeholder stays readable, which is what tells “waiting” apart from “refused”. What is left to the author is the option LABELS and qualifiers — they are all a person has to tell the options apart — and the placeholder wording, which is what says nothing has been chosen. With multiple, the boxes sit in a role="group" named by the label and described by the message, each a real checkbox; a required group that has been used and left empty is marked aria-invalid and its message says so.
Examples
Section titled “Examples”Options as data
Section titled “Options as data”The list handed over as value/label pairs, with a placeholder holding the “nothing chosen yet” state.
{ "id": "example", "section": "Options as data", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Order type", "placeholder": "Choose a type", "options": [ { "value": "standard", "label": "Standard" }, { "value": "renewal", "label": "Renewal" }, { "value": "replacement", "label": "Replacement" } ] } } ]}<caos-picklist-field id="picklistField1" label="Order type" placeholder="Choose a type"></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "standard", "label": "Standard" }, { "value": "renewal", "label": "Renewal" }, { "value": "replacement", "label": "Replacement" }];Options declared in markup
Section titled “Options declared in markup”The same field with its options written as <option> children instead — read once at connect, and the form that survives being written as plain markup.
Cannot be shown as a page placement:
<caos-picklist-field label="Order type" placeholder="Choose a type"> <option value="standard">Standard</option> <option value="renewal">Renewal</option> <option value="replacement">Replacement</option></caos-picklist-field>Chosen, and refused
Section titled “Chosen, and refused”A field with a value already selected beside one the page has refused — the same control, and the only thing separating them is what the page decided.
{ "id": "example", "section": "Chosen, and refused", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Order type", "value": "renewal", "options": [ { "value": "standard", "label": "Standard" }, { "value": "renewal", "label": "Renewal" } ] } }, { "id": "picklist_field_2", "type": "component", "key": "picklist_field", "inputs": { "label": "Region", "placeholder": "Choose a region", "required": true, "invalid": true, "message": "A region is needed before this order can be submitted.", "options": [ { "value": "north", "label": "North" }, { "value": "south", "label": "South" } ] } } ]}<caos-picklist-field id="picklistField1" label="Order type" value="renewal"></caos-picklist-field><caos-picklist-field id="picklistField2" label="Region" placeholder="Choose a region" required invalid message="A region is needed before this order can be submitted."></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');const picklistField2 = document.getElementById('picklistField2');picklistField1.options = [ { "value": "standard", "label": "Standard" }, { "value": "renewal", "label": "Renewal" }];picklistField2.options = [ { "value": "north", "label": "North" }, { "value": "south", "label": "South" }];One dropdown waiting on another
Section titled “One dropdown waiting on another”The Event field renders with no choices and its placeholder still showing until an Event kind is picked, then populates through an injected optionsSource — the cascading behaviour is wired by the page, never coded into the piece.
The page around it: wires the Event field to the Event kind field beside it: an optionsSource answering from the kind, and dependsOnValue kept in sync with the kind field’s own value on every change.
{ "id": "example", "section": "One dropdown waiting on another", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Event kind", "placeholder": "Choose a kind", "options": [ { "value": "created", "label": "Created" }, { "value": "updated", "label": "Updated" } ] } }, { "id": "picklist_field_2", "type": "component", "key": "picklist_field", "inputs": { "label": "Event", "placeholder": "Choose an event kind first", "depends_on": "event_kind" } } ]}<caos-picklist-field id="picklistField1" label="Event kind" placeholder="Choose a kind"></caos-picklist-field><caos-picklist-field label="Event" placeholder="Choose an event kind first" depends-on="event_kind"></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "created", "label": "Created" }, { "value": "updated", "label": "Updated" }];A long list, told apart by what each one is
Section titled “A long list, told apart by what each one is”Filterable draws a qualifier under each option and searches both lines when typing — the difference between a usable picker and an alphabetical wall once a set runs to hundreds of entries.
{ "id": "example", "section": "A long list, told apart by what each one is", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Object", "placeholder": "Search objects…", "filterable": true, "options": [ { "value": "account", "label": "Account", "qualifier": "Standard object" }, { "value": "contact", "label": "Contact", "qualifier": "Standard object" }, { "value": "acme__project", "label": "Project", "qualifier": "Custom object" }, { "value": "acme__shipment", "label": "Shipment", "qualifier": "Custom object" } ] } } ]}<caos-picklist-field id="picklistField1" label="Object" placeholder="Search objects…" filterable></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "account", "label": "Account", "qualifier": "Standard object" }, { "value": "contact", "label": "Contact", "qualifier": "Standard object" }, { "value": "acme__project", "label": "Project", "qualifier": "Custom object" }, { "value": "acme__shipment", "label": "Shipment", "qualifier": "Custom object" }];Several values
Section titled “Several values”Multiple set: the ways a contact may be reached, as one box per option, with email already ticked.
{ "id": "example", "section": "Several values", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Contact by", "multiple": true, "options": [ { "value": "email", "label": "Email" }, { "value": "phone", "label": "Phone" }, { "value": "sms", "label": "Text message" }, { "value": "post", "label": "Post" } ], "values": [ "email" ] } } ]}<caos-picklist-field id="picklistField1" label="Contact by" multiple></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "email", "label": "Email" }, { "value": "phone", "label": "Phone" }, { "value": "sms", "label": "Text message" }, { "value": "post", "label": "Post" }];picklistField1.values = [ "email"];Several values, one required
Section titled “Several values, one required”A required group: clear the one ticked box and it turns red and says a choice is needed.
{ "id": "example", "section": "Several values, one required", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Regions served", "multiple": true, "required": true, "options": [ { "value": "north", "label": "North" }, { "value": "south", "label": "South" }, { "value": "east", "label": "East" } ], "values": [ "north" ] } } ]}<caos-picklist-field id="picklistField1" label="Regions served" multiple required></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "north", "label": "North" }, { "value": "south", "label": "South" }, { "value": "east", "label": "East" }];picklistField1.values = [ "north"];Several values, not changeable
Section titled “Several values, not changeable”The same group disabled: what is chosen still reads clearly, and none of the boxes can be changed.
{ "id": "example", "section": "Several values, not changeable", "columns": 1, "items": [ { "id": "picklist_field_1", "type": "component", "key": "picklist_field", "inputs": { "label": "Contact by", "multiple": true, "disabled": true, "options": [ { "value": "email", "label": "Email" }, { "value": "phone", "label": "Phone" }, { "value": "sms", "label": "Text message" } ], "values": [ "email", "sms" ] } } ]}<caos-picklist-field id="picklistField1" label="Contact by" multiple disabled></caos-picklist-field>// What an attribute cannot hold, set as a property — the same values the preview is drawn with.const picklistField1 = document.getElementById('picklistField1');picklistField1.options = [ { "value": "email", "label": "Email" }, { "value": "phone", "label": "Phone" }, { "value": "sms", "label": "Text message" }];picklistField1.values = [ "email", "sms"];