Skip to content
Liquidify
HomeDocsComponentsExamplesPlayground
⌘K
(opens in new tab)
Liquidify

Accessible React controls with a CSS and SVG material engine.

Product

ComponentsExamplesPlaygroundPackages

Learn

DocumentationGetting startedConceptsAI resources

Project

CommunityShowcaseVersionsGitHub

Independent open-source software. Not affiliated with or endorsed by Apple Inc.

LicenseBrand

Component catalog

Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.

TextField

A user-editable control for entering freeform text — the canonical glass input field.

import { TextField } from "@liquidify/react/text-field"

Overview

Basic Text Field

A focused starting point showing Text Field with its essential configuration.

API

PropTypeDefaultDescription
autoCompletestring | undefined—Native `autocomplete` passthrough.
classNamestring | undefined—Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.
clearableboolean | undefinedfalseWhen `true`, a trailing clear button renders whenever the resolved value is non-empty and the field is neither `disabled` nor `readOnly`. Pressing it commits `""` through the controlled triad, fires {@link TextFieldChromeProps.onClear}, and returns focus to the field. `Escape` takes the same path.
clearLabelstring | undefinedClear textAccessible name of the clear button.
defaultValuestring | undefined""Uncontrolled seed for the internal value (ignored once {@link TextSurfaceProps.value} is supplied). Read once, on mount.
disabledboolean | undefined—When `true`, the native `disabled` attribute is set on the inner element: the surface is removed from the tab order and all input is suppressed. `aria-disabled` is NOT set — the native attribute already provides full-suppression semantics.
distribution"fill" | "fit" | undefinedfillLayout of the field's DOM root — the TextField sibling of SegmentedControl's `distribution` axis (`NSSegmentedControl.Distribution` pedigree, charter §5). `"fill"` fills the container width; `"fit"` hugs its content. Resolved via the `data-distribution` attribute selector in `styles.css`.
iconReactNode—Optional decorative node rendered before the text — the in-field leading glyph (a magnifier for a search field), following the `Label` / `EmptyState` icon convention. Rendered `aria-hidden`, so it is never the accessible name: still supply `aria-label` / `aria-labelledby`. On the multiline branch it aligns to the first text row rather than the box's vertical centre.
idstring | undefined—Explicit id for the native element. Falls back to a generated stable id so an external `<label htmlFor>` or error message can target it (A16).
inputModeTextInputMode | undefined—Native `inputmode` passthrough — hints the on-screen keyboard variant.
invalidboolean | undefined—Marks the surface invalid — sets `aria-invalid="true"` for AT / validation styling. An explicit `aria-invalid` on the same element always wins (including `false` / `"grammar"` / `"spelling"`); `invalid` is only the shorthand fallback.
lineLimitLineLimit | undefined{ min: 2, max: 4 }Not on the single-line branch — `lineLimit` is multiline-only. See {@link TextFieldSingleLineProps.sizing}. Row-range bound — maps to SwiftUI's `.lineLimit(min...max)`. See {@link LineLimit} for the sanitisation policy applied at render time.
maxLengthnumber | undefined—Native `maxlength` passthrough.
minLengthnumber | undefined—Native `minlength` passthrough.
multilineboolean | undefined—Discriminant: absent/`false` selects the single-line (`<input>`) branch. Discriminant: `true` selects the multiline (`<textarea>`) branch.
namestring | undefined—Native form field name. When set, the element participates in form submission and React Hook Form can register it (A16).
onBlur((event: FocusEvent<HTMLTextAreaElement, Element>) => void) | ((event: FocusEvent<HTMLInputElement, Element>) => void) | undefined—Low-level native passthrough; fires on native blur of the element. Never fires while {@link TextSurfaceProps.disabled} is `true`.
onChange((next: string) => void) | undefined—Fires with the next string whenever the content changes — the SwiftUI `Binding<String>` write-back analogue. Called in both controlled and uncontrolled modes; never called while {@link TextSurfaceProps.disabled} or {@link TextSurfaceProps.readOnly}.
onClear(() => void) | undefined—Fires after the value is cleared via the clear button or `Escape`.
onFocus((event: FocusEvent<HTMLTextAreaElement, Element>) => void) | ((event: FocusEvent<HTMLInputElement, Element>) => void) | undefined—Low-level native passthrough; fires on native focus of the element. Never fires while {@link TextSurfaceProps.disabled} is `true`.
onKeyDown((event: KeyboardEvent<HTMLInputElement>) => void) | ((event: KeyboardEvent<HTMLTextAreaElement>) => void) | undefined—Low-level native passthrough; fires on `keydown` of the `<input>`, **before** the component's own `Enter` / `Escape` handling — calling `event.preventDefault()` here vetoes it. Never fires while {@link TextSurfaceProps.disabled} is `true`. Low-level native passthrough; fires on `keydown` of the `<textarea>`, **before** the component's own `Escape` handling. See {@link TextFieldSingleLineProps.onKeyDown}.
onSubmit((value: string) => void) | undefined—Fires with the current value when `Enter` is pressed — the SwiftUI `.onSubmit { … }` analogue and the search field's submit affordance. Native form submission is untouched (the component never calls `preventDefault`). Never fires while `disabled` or `readOnly`, nor when a caller's {@link TextFieldSingleLineProps.onKeyDown} prevented the event. Single-line only — multiline `Enter` inserts a newline. Not on the multiline branch — `Enter` inserts a newline in a `<textarea>`, so there is no submit key to hang `onSubmit` on. See {@link TextFieldMultilineProps.secure}.
patternstring | undefined—Native validation pattern (a `RegExp` source string) — single-line only, the SwiftUI surface has no textarea pattern analogue. Not on the multiline branch — `pattern` is single-line-only. See {@link TextFieldMultilineProps.secure}.
placeholderstring | undefined—Placeholder text shown when the surface is empty — the SwiftUI title-arg analogue (the `prompt` parameter).
readOnlyboolean | undefined—When `true`, the surface stays focusable and its value is submitted, but no edit is possible: the native `readOnly` attribute is set and `onChange` is hard-suppressed (never a silent CSS-only block).
ref((instance: HTMLTextAreaElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | RefObject<HTMLTextAreaElement | null> | ((instance: HTMLInputElement | null) => void | (() => VoidOrUndefinedOnly) | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | RefObject<HTMLInputElement | null> | null | undefined—Ref forwarded to the native element — the surface's primary interactive element.
requiredboolean | undefined—Marks the surface required — sets the native `required` + `aria-required`.
secureboolean | undefinedfalseWhen `true`, the element is `<input type="password">` and entered text renders as platform masked dots. Single-line only — combining with `multiline` is a type error (was silently ignored pre-Phase-3). Not on the multiline branch — `secure` is single-line-only (SwiftUI has no `SecureField(axis: .vertical)`). Typed `never` for the same real- type-error reason as {@link TextFieldSingleLineProps.sizing}.
sizingTextFieldSizing | undefinedrelativeNot on the single-line branch — `sizing` is multiline-only. Typed `never` (rather than simply absent) so a caller combining it with a single-line field is a real type error, not a leniency gap in TypeScript's excess- property checking against union types. Multiline height behaviour — `"fixed"` locks to `lineLimit.min` rows, `"relative"` grows `min`→`max` rows then scrolls, `"content"` grows to fit all content. Resolved via `data-sizing` in CSS (multiline-only attribute).
tintLiquidTintValue | undefined—System-accent of the control — drawn from the RESTRICTED {@link LIQUID_COLORS} palette (the thirteen Apple system colours). **Orthogonal to a component's `variant`**: it recolours the accent the surface draws from (focus ring, tinted fill, selection) without changing its variant. Resolved via `data-tint` attribute selectors in CSS (tokens-only, ADR-0003).
typeTextFieldType | undefinedtextNative `type` of the `<input>` — single-line only. `"search"` is the search-field setting (the browser's own cancel button is suppressed in CSS so {@link TextFieldChromeProps.clearable} stays the single clear affordance). {@link TextFieldSingleLineProps.secure} wins when both are supplied. Not on the multiline branch — a `<textarea>` has no `type` attribute. Typed `never` for the same real-type-error reason as {@link TextFieldMultilineProps.secure}.
valuestring | undefined—Controlled current text value. Passing this switches the surface to controlled mode: the rendered value always reflects this prop and the consumer owns it via {@link TextSurfaceProps.onChange}.
variantTextFieldVariant | undefinedroundedVisual style of the field (the SwiftUI `.textFieldStyle(…)` family). `"automatic"`/`"plain"` are bare text containers (no glass); `"rounded"` draws the translucent-glass rounded-rect shell; `"prominent"` is a full-pill glassy "main field" for the one or two hero fields per screen.

Guidelines

Do
  • Use Text Field when its semantics match the content or action.
  • Keep labels concise, specific, and consistent with the surrounding interface.
  • Test the component with keyboard navigation, zoom, and both color schemes.
Don't
  • Do not use Text Field as a visual substitute for a different semantic element.
  • Do not rely on color or an icon alone to communicate essential meaning.
  • Do not override the component tokens with fixed values unless the product theme requires it.

Accessibility

TextField supports the documented keyboard interactions below.

Keyboard
  • Press Tab to move focus to the control.
  • Use the control's documented activation keys without trapping focus.
  • Provide an accessible name whenever the visible label is absent.

Tokens

NameValue
--lq-color-accent#007aff
--lq-color-bg-primary#ffffff
--lq-color-fill-primary-darkComponent-defined value
--lq-color-fill-primary-lightComponent-defined value
--lq-color-label-primary-dark#ffffff
--lq-color-label-primary-light#000000
--lq-color-label-secondary-darkrgba(235, 235, 245, 0.6)
--lq-color-label-secondary-lightrgba(60, 60, 67, 0.6)
--lq-color-separatorrgba(60, 60, 67, 0.14)
--lq-color-system-blue-default-dark#0091ff
--lq-color-system-blue-default-light#0088ff
--lq-duration-0ms80ms120ms150ms250ms350ms500ms
--lq-easing-cubic-bezier(0.25, 0.1, 0.25, 1)cubic-bezier(0, 0, 0.2, 1)cubic-bezier(0.4, 0, 1, 1)cubic-bezier(0.175, 0.885, 0.32, 1.275)cubic-bezier(0.25, 0.46, 0.45, 0.94)
--lq-font-family-sansInter, Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif
--lq-font-size-body0.8125rem
--lq-glass-440.71.15var(--lq-segmented-control-indicator-blur)calc(var(--lq-glass-material-blur) * 8)var(--lq-shadow-presentational)
--lq-hit-target-touch2.75rem
--lq-lens-Component-defined value
--lq-letter-spacing-body-0.025625rem
--lq-line-height-body1.25
--lq-radius-mdComponent-defined value
--lq-radius-pill9999rem
--lq-space-0.0625rem0.125rem0.25rem0.5rem0.75rem1rem1.25rem1.5rem2rem2.5rem3rem3.5rem
--lq-space-161rem
--lq-text-field-padvar(--lq-space-8)var(--lq-space-12)
--lq-text-field-slot-gapvar(--lq-space-8)

Key features

  • Liquid material

    Uses Liquidify surfaces and theme-aware design tokens.

  • Typed API

    Provides a typed React API with generated prop documentation.

  • Accessible by default

    Preserves semantic structure, focus visibility, and assistive-technology state.

Resources

  • Source
  • Markdown
  • JSON
  • Storybook
Components