Skip to content

BaseField

the shared implementation behind the single-line input primitives; not an element of its own

Not placed on a page at all — this piece is used by other pieces and is offered nowhere.

Name Attribute Description Type Default Required
label label, markup only The field’s name, drawn above the control. The label carries a for pointing at a generated input id, so clicking it focuses the control. Left unset, the label collapses and nothing is drawn. text none declared Optional
placeholder placeholder, markup only Forwarded to the inner input as its placeholder, and removed again when the attribute goes. text none declared Optional
value value, also a property The current value. It is two-way: the shared implementation writes it back on every keystroke, so the attribute and the control never disagree. Reading it back gives the live contents of the inner control — what the person has typed. Writing goes the other way, to the attribute, which the control then follows. The two agree because the shared implementation writes the attribute back on every keystroke; they are not the same thing being read and written, which is worth knowing for anything that sets the attribute and reads the property in the same breath. text none declared Optional
name name, markup only The form name forwarded to the inner input. text none declared Optional
min min, markup only Forwarded to the inner input. Meaningful where the input type has a floor — a number or a date — and ignored by a text input. text none declared Optional
max max, markup only Forwarded to the inner input. Meaningful for a number or a date; ignored by a text input. text none declared Optional
step step, markup only Forwarded to the inner input. The increment for a number, the day interval for a date; ignored by a text input. text none declared Optional
required required, markup only Draws the asterisk beside the label — marked hidden from a screen reader, because the input already reports required itself — and sets required on the inner input. boolean false Optional
disabled disabled, markup only Sets disabled on the inner input: greyed, not focusable, not submitted. boolean false Optional
invalid invalid, markup only Turns the outline and the message red and sets aria-invalid on the input. Nothing in the shared implementation decides validity; it only draws the state it is given. boolean false Optional
message message, markup only The line under the control. Muted on its own, red when invalid is also set. Empty collapses, so an unset message takes no space. text none declared Optional
inline inline, markup only Switch from the boxed form rendering to the record-detail one: no box, an accent underline, inheriting the surrounding font and size, with the label and the message hidden. Honoured through the stylesheet rather than through the attribute callback, so it is not in observedAttributes — it works all the same. boolean false Optional
numeric numeric, markup only Draw the value in tabular figures so a column of them lines up. NumberField sets this on itself when it connects; the others do not, and there is rarely a reason to set it by hand. boolean false Optional
Name When it fires What it carries
input On every keystroke, after the value attribute has been written back. nothing
change When the inner input commits — focus leaves, Enter, or a picker is used. nothing

Nothing goes inside this piece.

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 value.
--caos-space-1 The gap between the label, the control and the message.
--caos-space-3 The vertical padding inside the control.
--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, at rest and disabled.
--caos-color-text The colour of the value.
--caos-color-text-muted The label, the placeholder, 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.
Name Description Arguments
focus() Focus the inner control and select what is already in it, so typing replaces rather than appends. The selection is attempted and swallowed on the input types that do not support it, which is how one method serves all three subclasses. options: FocusOptions

Nothing on this piece can be read back.

Part Which piece of it When it is there
::part(label) the label above the control Always
::part(input) the inner input element Always
::part(message) the line under the control Always

Read it when you want the shared contract in one place, or when you are adding another single-line input to the library — a new one is a subclass passing its input type to super(), and should be nothing more.

  • TextField — You want the shared field with a text input in it.
  • NumberField — You want it with a number input, tabular figures, and min/max/step that mean something.
  • DateField — You want it with a date input and the platform calendar.
  • FieldRenderer — The type is not known until the record is read.
  • CheckboxField — The value is a yes-or-no — it is a separate implementation, not one of these.
  • PicklistField — The value is one of a fixed list — also a separate implementation.

One generated id per instance joins the label to the control, which is the whole reason it is generated: without it a screen reader announces an unnamed text box and clicking the label does nothing. required and invalid are forwarded to the input as required and aria-invalid, and the asterisk beside the label is hidden from assistive technology because the input already reports the same fact. Focus is drawn as a wash inside the control rather than as an outline ring. What is left to the author, in every subclass, is the label text and the wording of the message.

The same template three times over — one label, one box, one message — differing only by the input type each passes up.

{
"id": "example",
"section": "The three primitives that are it",
"columns": 1,
"items": [
{
"id": "text_field_1",
"type": "component",
"key": "text_field",
"inputs": {
"label": "Project name",
"value": "North region rollout"
}
},
{
"id": "number_field_2",
"type": "component",
"key": "number_field",
"inputs": {
"label": "Hours",
"value": 48,
"step": 0.25
}
},
{
"id": "date_field_3",
"type": "component",
"key": "date_field",
"inputs": {
"label": "Due date",
"value": "2026-09-15"
}
}
]
}
<caos-text-field label="Project name" value="North region rollout"></caos-text-field>
<caos-number-field label="Hours" value="48" step="0.25"></caos-number-field>
<caos-date-field label="Due date" value="2026-09-15"></caos-date-field>

Required, refused and locked, one on each subclass — the asterisk, the red outline with its reason, and the greyed control all come from the shared implementation rather than from any of the three.

{
"id": "example",
"section": "The states they share",
"columns": 1,
"items": [
{
"id": "text_field_1",
"type": "component",
"key": "text_field",
"inputs": {
"label": "Project name",
"required": true,
"value": "North region rollout"
}
},
{
"id": "number_field_2",
"type": "component",
"key": "number_field",
"inputs": {
"label": "Hours",
"value": -6,
"min": 0,
"invalid": true,
"message": "Hours cannot be negative."
}
},
{
"id": "date_field_3",
"type": "component",
"key": "date_field",
"inputs": {
"label": "Opened",
"value": "2026-08-04",
"disabled": true
}
}
]
}
<caos-text-field label="Project name" required value="North region rollout"></caos-text-field>
<caos-number-field label="Hours" value="-6" min="0" invalid message="Hours cannot be negative."></caos-number-field>
<caos-date-field label="Opened" value="2026-08-04" disabled></caos-date-field>

The same three with inline set: the box is gone, an accent underline is left, and the type is inherited from the surrounding text — which is why the label and the message are hidden here and the record detail supplies them.

{
"id": "example",
"section": "The record-detail variant",
"columns": 1,
"items": [
{
"id": "text_field_1",
"type": "component",
"key": "text_field",
"inputs": {
"inline": true,
"value": "North region rollout"
}
},
{
"id": "number_field_2",
"type": "component",
"key": "number_field",
"inputs": {
"inline": true,
"value": 48,
"step": 0.25
}
},
{
"id": "date_field_3",
"type": "component",
"key": "date_field",
"inputs": {
"inline": true,
"value": "2026-09-15"
}
}
]
}
<caos-text-field inline value="North region rollout"></caos-text-field>
<caos-number-field inline value="48" step="0.25"></caos-number-field>
<caos-date-field inline value="2026-09-15"></caos-date-field>