Component catalog
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
A user-editable control for entering freeform multi-paragraph text — the canonical glass long-form editor.
import { TextEditor } from "@liquidify/react/text-editor"| Prop | Type | Default | Description |
|---|---|---|---|
| autoComplete | string | undefined | — | Native `autocomplete` passthrough. |
| className | string | 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. |
| defaultValue | string | undefined | "" | Uncontrolled seed for the internal value (ignored once {@link TextSurfaceProps.value} is supplied). Read once, on mount. |
| disabled | boolean | 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. |
| id | string | 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). |
| inputMode | TextInputMode | undefined | — | Native `inputmode` passthrough — hints the on-screen keyboard variant. |
| invalid | boolean | 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. |
| lineLimit | LineLimit | undefined | { min: 6, max: 12 } | Row-range bound. `lineLimit.min` is the region's initial height and drives the native `rows` attribute; `lineLimit.max` caps growth under `sizing="relative"` and is inert under the other two modes. See {@link LineLimit} for the sanitisation policy applied at render time. |
| maxLength | number | undefined | — | Native `maxlength` passthrough. |
| minLength | number | undefined | — | Native `minlength` passthrough. |
| name | string | 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) | 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}. |
| onFocus | ((event: FocusEvent<HTMLTextAreaElement, Element>) => void) | undefined | — | Low-level native passthrough; fires on native focus of the element. Never fires while {@link TextSurfaceProps.disabled} is `true`. |
| placeholder | string | undefined | — | Placeholder text shown when the surface is empty — the SwiftUI title-arg analogue (the `prompt` parameter). |
| readOnly | boolean | 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 | Ref<HTMLTextAreaElement> | undefined | — | Ref forwarded to the native element — the surface's primary interactive element. |
| required | boolean | undefined | — | Marks the surface required — sets the native `required` + `aria-required`. |
| resizable | TextEditorResizable | undefined | none | Whether the reader may drag the region taller. `"vertical"` exposes the native resize handle; the inline axis is never resizable, so the region can never break the enclosing layout's width. Precedence, deliberately documented rather than silently enforced: where the browser supports `field-sizing: content` the UA's auto-sizing wins under `sizing="relative"` / `"content"`, which makes the handle inert or puts it in tension with the `lineLimit.max` cap; and a reader's drag writes an inline height that may exceed that cap. The handle is therefore most meaningful with `sizing="fixed"`. It is not gated on the sizing mode — a prop that silently no-ops under three of four values is worse than a documented precedence rule. |
| sizing | TextEditorSizing | undefined | fixed | Height behaviour of the region (**Q2 SUPERSEDED** — the editor adopts TextField's multiline height vocabulary rather than a bare `rows` count, so the two surfaces cannot disagree on what a row range means). Resolved via `data-sizing` in CSS. |
| tint | LiquidTintValue | 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). |
| value | string | 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}. |
TextEditor supports the documented keyboard interactions below.
| Name | Value |
|---|---|
| --lq-color-accent | #007aff |
| --lq-color-bg-primary | #ffffff |
| --lq-color-fill-primary-dark | Component-defined value |
| --lq-color-fill-primary-light | Component-defined value |
| --lq-color-label-primary-dark | #ffffff |
| --lq-color-label-primary-light | #000000 |
| --lq-color-label-secondary-dark | rgba(235, 235, 245, 0.6) |
| --lq-color-label-secondary-light | rgba(60, 60, 67, 0.6) |
| --lq-color-separator | rgba(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-sans | Inter, Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif |
| --lq-font-size-body | 0.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-touch | 2.75rem |
| --lq-lens- | Component-defined value |
| --lq-letter-spacing-body | -0.025625rem |
| --lq-line-height-body | 1.25 |
| --lq-radius-18 | 1.125rem |
| --lq-radius-md | Component-defined value |
| --lq-space- | 0.0625rem0.125rem0.25rem0.5rem0.75rem1rem1.25rem1.5rem2rem2.5rem3rem3.5rem |
| --lq-text-editor-pad | var(--lq-space-8) |
| --lq-text-editor-rows-max | Resolved by the active Liquidify theme |
| --lq-text-editor-rows-min | Resolved by the active Liquidify theme |
| --lq-text-field-rows- | Component-defined value |