Skip to main content
Gelium UI
Blog View source 0.6.5 Theme Gallery
Recipe

Native behavior · Default for behavior · Gelium defaults · Native baseline

Appearance
Menu

Published · Updated · v0.6.5 · MIT License · Source: text-field.md

Text field

We'll only use this for account updates.

Tell people a little about yourself.

Required; we check the format on submit.

3–20 lowercase letters, numbers, or underscores.

Enter your name.

Text field is a single- or multi-line input built on native <input> and <textarea> elements. Use a text field when people must enter or edit free-form text — names, email addresses, search terms. The value must submit through a real <form>. Every visible label is explicitly associated through for and id, while helper and error messages use unique IDs referenced by aria-describedby.

Examples

Outlined and filled

Outlined and filled fields cover the two visual variants of the component.

{{template "text-field" .}}

Native input surface

Type, constraints, and autofill hints pass through as native attributes.

We check the format on submit.

{{template "text-field" .}}

Helper and error messages

Helper text and visible errors are associated through aria-describedby, never color alone.

We'll only use this for account updates.

{{template "text-field" .}}

Server-side validation

The validation form posts to the server and swaps the fragment on HTTP 422.

Enter your name.

{{template "validation-form" .}}

API reference

Text field props
PropTypeDefaultDescription
IDstring""Element id paired with the label's for attribute.
Labelstring""Visible label text, explicitly associated via for and id.
Namestring""Form submission name.
Valuestring""Initial input or textarea value.
Variantstring""Field style: outlined or filled.
Helperstring""Helper message referenced by aria-describedby.
MessageRolestring""ARIA role for the helper message, for example status.
Errorstring""Error message; renders aria-invalid and a visible alert.
DisabledboolfalseNative disabled attribute; takes precedence over error.
TextareaboolfalseRenders a multi-line textarea instead of an input.
AutofocusboolfalseNative autofocus attribute.
Typestring"text"Native input type: email, url, tel, number, password, search, or text. Inputs only.
RequiredboolfalseNative required attribute; the value must be submitted.
MaxLengthint0Native maxlength in characters; 0 omits the attribute.
MinLengthint0Native minlength in characters; 0 omits the attribute.
Patternstring""Native pattern regex for inputs; textareas do not support it.
Autocompletestring""Standard autocomplete token (email, current-password, …).
InputModestring""Native inputmode hint for the on-screen keyboard.
ReadOnlyboolfalseNative readonly attribute; focusable and submitted, not editable.
Placeholderstring""Visible hint text; keeps the floating label floated and never replaces it.

Guidance

When to use

Use a text field when people must enter or edit free-form text — names, email addresses, search terms. The value must submit through a real <form>.

When not to use

Do not use a text field when a constrained choice is faster. One option from a known list belongs in a Select or Radio group. An approximate magnitude belongs in a Slider. For short, predefined answers, Chips can replace typing.

Usability

  • Single-line inputs use <input>; longer answers use the multi-line <textarea>.
  • Every visible label is explicitly associated through for and id; helper and error messages use unique IDs referenced by aria-describedby.
  • Server-side validation returns HTTP 422 with the submitted value preserved and aria-invalid="true" on the field.

Accessibility

  • The native <input>/<textarea> keeps its role, name, value, and keyboard behavior at no cost.
  • Every visible label is explicitly associated through for and id, matching what is on screen.
  • Errors are announced via aria-invalid plus visible error text — never color alone.
  • Disabled keeps the native disabled attribute and takes precedence over error.

See Choose the right control for the cross-component decision.

Alternative names

  • Input, text input, form field, entry field.

Agent prompt

Use Text field when people must enter or edit free-form text — names, email addresses, search terms. The value must submit through a real <form>. It is built on native <input> and <textarea> elements. Every visible label is associated through for/id, and helper and error messages are referenced by aria-describedby. Server-side validation returns HTTP 422 with the submitted value preserved, aria-invalid="true", and the X-Gelium-Validation: true header for the HTMX swap hook. Never build a custom text-entry widget: the native control is the contract, disabled keeps the native disabled attribute, and disabled takes precedence over error.

Variants and states

The dogfooded examples demonstrate outlined and filled variants, plus normal and helper text. They include a visibly labelled error that does not rely on color. They cover disabled inputs (filled and outlined), a disabled textarea, and an editable textarea. The preview also dogfoods the native input surface: typed inputs, constraints, autofill hints, and a real placeholder.

Disabled fields keep the native disabled attribute: they leave the tab order, cannot receive focus or edits, and are excluded from hover and focus states. Disabled takes explicit precedence over error: a field flagged disabled never advertises aria-invalid or an error alert, even when both states are set. The CSS also resolves a combined ui-text-field-error ui-text-field-disabled wrapper to the disabled palettes instead of the error palette.

Input surface

Single-line fields map to the native <input>; multi-line ones to the <textarea>. The view passes a set of native attributes through, rendering each only when set.

  • Type switches the input kind: email, url, tel, number, password, search, or the default text. Textareas ignore it, and so does Pattern, which is input-only.
  • InputMode hints at the on-screen keyboard. Pair it with Type per the Forms contract table.
  • Autocomplete carries a standard token for identity and contact data, like email or current-password.
  • Required, MaxLength, MinLength, and Pattern express native constraints. Server validation stays authoritative on submit.
  • ReadOnly keeps the field focusable and submitted, but not editable. Disabled instead leaves the tab order.
  • Placeholder shows hint text without replacing the visible label. The floating label stays floated so hint and label never overlap.

See Forms contract for the type and inputmode pairing table, autocomplete tokens, and validation timing.

How does server-side validation work?

The form below submits a named field to the server with browser validation disabled so the server remains the source of truth.

Whitespace-only input returns HTTP 422 with the submitted value preserved, aria-invalid="true", and an associated error. Valid input returns HTTP 200 with the preserved value and an accessible success message.

The form works without JavaScript: its native method and action submit to the same endpoint. The endpoint returns a complete documentation page with the updated form, preserving the HTTP 422 or 200 status. HTMX (the progressive-enhancement library that swaps server-rendered fragments) is an optional layer. When it sends HX-Request: true, the server returns only the complete form fragment and replaces it using hx-target="this" plus hx-swap="outerHTML".

Expected HTMX validation failures return X-Gelium-Validation: true. Local /static/app.js listens for htmx:before:swap. It reads the HTMX 4 response from event.detail.ctx.response. For status 422 with that header, it sets shouldSwap to true and isError to false. Other HTTP 422 responses remain transport errors and are not swapped by this hook.