Skip to content

LookupField

<caos-lookup-field>

a field pointing at another record — reads as a link, re-points through a typeahead picker

May be placed on a record page, a page inside an app and a step of a guided flow.

Name Attribute Description Type Default Required
mode mode, markup only Which half to draw: read for the link, edit for the typeahead. Anything else reads as read. text — recognises read, edit; anything else reads as read read Optional
search property only The result source — (query, request) => options or a promise of them, where an option is { value, label, sublabel?, href?, fields? }. The second argument carries what the surface asked for, including the fields the query should be matched against; a source written for the one-argument shape keeps working and simply ignores it. Set it as a property. Whatever it returns is offered, so it is this function that has to be scoped to what the current person may read. json none declared Optional
matchFields match-fields, markup only Which fields the typing is matched against, as a comma-separated list handed straight to the search seam — “name, account_number”. A lookup that matches only on a name cannot find a record by its number, and the surface is what knows which is which. Saying nothing leaves the decision to the source, which is what every source decided on its own before. text none declared Optional
displayFields display-fields, markup only Which of a result’s own fields are drawn on its second line, as a comma-separated list — “industry, city” — joined in the order given. This is how two records with the same name are told apart. An option that supplies an explicit sublabel keeps it: a source that has already decided what the second line says was not asked to reconsider. text none declared Optional
minChars min-chars, markup only How much has to be typed before the source is asked at all. Under it the list says what is needed rather than offering everything. Zero — ask from the first keystroke, and from an empty box — is what this control did before and is still the default. number 0 Optional
searchDelay search-delay, markup only Milliseconds to wait after the last keystroke before asking, so a fast typist makes one request rather than eight. Raise it for a source that is expensive to ask. number 150 Optional
value value, also a property The id of the record pointed at. Written back when an option is chosen, and removed on clear. recordId none declared Optional
display display, also a property The linked record’s name — what read mode actually draws. Without it the link falls back to showing the raw id, which is the one thing this piece exists to avoid. text none declared Optional
href href, markup only Where the read-mode link goes. Left unset the name is still drawn and still reachable by keyboard, as a trigger for re-pointing rather than as navigation. text none declared Optional
label label, markup only The field’s name, drawn above the control and pointed at the input, so clicking it focuses the typeahead. It is also used as the accessible name. text none declared Optional
placeholder placeholder, markup only Two jobs, one word: the hint inside the empty typeahead in edit mode, and what read mode shows when nothing is linked. Read mode falls back to an em dash. text Search… Optional
required required, markup only A record has to be pointed at. Draws the asterisk beside the label. boolean false Optional
disabled disabled, markup only The link may not be changed. Greys the typeahead and refuses focus. It does not affect read mode. boolean false Optional
invalid invalid, markup only The link is wrong or missing. Turns the outline and the message red and marks the input invalid. 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: no box, an accent underline, inheriting the surrounding type, with the search glyph and the clear control pulled to the edges. The label and the message are hidden. boolean false Optional
Name When it fires What it carries
caos-lookup-change When an option is chosen — clicked, or Enter on the active one. The list closes and the input takes the chosen name. { value, label } — the id now pointed at and the name being shown for it.
caos-lookup-clear When the clear control is pressed. The link is dropped, the input is emptied and focus returns to it. nothing

Nothing goes inside this piece.

Design token What it controls
--caos-font The typeface of the label, the link, the typeahead and the message.
--caos-text-body The size of the label, the link and the typed query.
--caos-text-row The size of an option label in the list.
--caos-space-1 The gap between the label, the control and the message, and the padding inside the list.
--caos-space-2 The inset of the clear control, and the vertical padding of an option.
--caos-space-3 The padding inside the typeahead and the horizontal padding of an option.
--caos-space-4 The left inset of the typeahead in the inline variant, clearing its search glyph.
--caos-space-5 With the smaller spaces, the room the typeahead keeps for its glyph and its clear control.
--caos-radius The corner radius of the typeahead and of the results list.
--caos-radius-sm The corner radius of an option, of the clear control and of the link focus ring.
--caos-color-border The outline of the typeahead at rest, and the edge of the results list.
--caos-color-border-strong The outline of the typeahead while it has focus.
--caos-color-surface The fill of the results list, and the ground the selected option is mixed into.
--caos-color-surface-2 The fill of the typeahead, and the highlight on the option the keyboard is on.
--caos-color-text The typed query and an option label.
--caos-color-text-muted The field label, the search glyph, the placeholder, an option sublabel, the status line and the message.
--caos-color-accent The hover outline, the underline of the inline variant, and the tint on the already-chosen option.
--caos-color-accent-strong The colour of the read-mode link.
--caos-accent-wash The tint that fills the typeahead while it has focus, in place of a focus ring.
--caos-focus-ring The ring on the read-mode link and on the clear control.
--caos-shadow-float The shadow that lifts the results list off the page.
--caos-color-danger The required asterisk, and the outline and message when invalid.
Name Description Arguments
focus() Move keyboard focus to the typeahead. Focusing it re-runs the search when there is already a query in it, so the list comes back rather than staying shut. options: FocusOptions
Name Type What it tells you
open boolean Whether the results list is showing.
Part Which piece of it When it is there
::part(label) the label above the control Always
::part(link) the linked record in read mode Always
::part(combo) the combobox in edit mode Always
::part(icon) the search glyph inside the box Always
::part(input) the text input Always
::part(clear) the control that clears the selection Always
::part(listbox) the results list Always
::part(option) one result Always
::part(message) the line under the control Always

The value is another RECORD, and there are too many of them to list. Read mode wherever a record shows what it is linked to; edit mode wherever that link can be changed.

  • PicklistField — The options are a short fixed list that can simply be handed over, rather than something searched.
  • FieldRenderer — The type is not known until the record is read — hand it the field metadata and it draws this piece for a Reference, in whichever mode it was given.
  • KeyValueRow — The link only has to be read and never re-pointed, and it sits among other read-only rows.
  • ObjectCaret — The person is navigating to a record rather than choosing one to store on this one.
  • TextField — What is being captured is somebody else’s reference number, not a record in this system.

The typeahead is a real combobox: role="combobox" with aria-expanded, aria-controls and aria-autocomplete="list" over a role="listbox" of options, and the arrow keys move an aria-activedescendant rather than moving DOM focus, so what the person is typing never leaves the box. Enter selects, Escape closes, Home and End jump to the ends, the list scrolls the active option into view, and the list closes when focus leaves the whole piece — including out of the shadow root, which is checked through the deep active element rather than assumed. The clear control carries its own name, the input has a generated id its label points at, and the searching and no-matches lines are real content in the list rather than an empty box. What is left to the author is the option LABELS and sublabels — two accounts with the same name are told apart only by the sublabel — and display, without which the link announces an id.

Read mode: the linked record drawn as its name, in the accent, never as the id underneath it.

{
"id": "example",
"section": "The link",
"columns": 1,
"items": [
{
"id": "lookup_field_1",
"type": "component",
"key": "lookup_field",
"inputs": {
"mode": "read",
"label": "Account",
"value": "acc-meridian",
"display": "Meridian Supply",
"href": "#"
}
}
]
}
<caos-lookup-field mode="read" label="Account" value="acc-meridian" display="Meridian Supply" href="#"></caos-lookup-field>

The same read mode with no record pointed at — an em dash, or whatever the placeholder says, rather than an empty gap.

{
"id": "example",
"section": "Nothing linked yet",
"columns": 1,
"items": [
{
"id": "lookup_field_1",
"type": "component",
"key": "lookup_field",
"inputs": {
"mode": "read",
"label": "Account"
}
},
{
"id": "lookup_field_2",
"type": "component",
"key": "lookup_field",
"inputs": {
"mode": "read",
"label": "Parent account",
"placeholder": "Not set"
}
}
]
}
<caos-lookup-field mode="read" label="Account"></caos-lookup-field>
<caos-lookup-field mode="read" label="Parent account" placeholder="Not set"></caos-lookup-field>

Edit mode doing its job: type a letter, the results open, the arrow keys roam them, and the clear control appears once something is linked.

The page around it: supplies the search — three accounts filtered by name. A placement stores values, and a function is not one, so the results always come from the page around this field rather than from the field itself.

{
"id": "example",
"section": "Re-pointing it",
"columns": 1,
"items": [
{
"id": "lookup_field_1",
"type": "component",
"key": "lookup_field",
"inputs": {
"mode": "edit",
"label": "Account",
"placeholder": "Search accounts…",
"value": "acc-meridian",
"display": "Meridian Supply"
}
}
]
}
<caos-lookup-field mode="edit" label="Account" placeholder="Search accounts…" value="acc-meridian" display="Meridian Supply"></caos-lookup-field>