Skip to content
ArcadiaDesign
A composer is a ghost lg Textarea in its own frame. Type a few lines: it grows, then scrolls past eight.

TSX

It grows from minRows (3 by default) to a cap, maxRows or maxHeight (which wins), and scrolls past it: CSS field-sizing: content, with a measured fallback. sm and md are UI text; lg is conversation text, as in the composer. Focus shows through the border, never a ring.

Examples

In a field

Inside Field, with a label and a hint.

In a form

Controlled with value and onValueChange, submitted natively. On a bad submit the error appears under the field, is tied to it, and focus goes back to it so it is read out.

Size and state

rootStyle takes where the textarea sits (margins, self-alignment, flex and grid placement, size bounds) and the knobs below, each a token of its kind. A colour, a raw length or any other property is a type error: a look that comes up twice becomes a variant.

TSX
--textarea-py
TokenRef<Space>Default var(--space-1-5)
Block padding (the min height counts it).
--textarea-px
TokenRef<Space>Default var(--space-2-5)
Inline padding.
--textarea-font
TokenRef<FontSize>Default var(--font-size-base)
Text size.
--textarea-lh
TokenRef<LineHeight>Default var(--line-height-base)
Line height (one row of minRows and maxRows).
--textarea-tracking
TokenRef<Tracking>Default var(--tracking-base)
Letter spacing.
--textarea-radius
TokenRef<Radius>Default var(--radius-row)
Corner radius.
--textarea-bg
PaintDefault transparent
Fill.
--textarea-fg
PaintDefault var(--text-1)
Text colour.
--textarea-fg-disabled
PaintDefault var(--text-4)
Text colour when disabled.
--textarea-placeholder
PaintDefault var(--text-3)
Placeholder colour.
--textarea-border
PaintDefault var(--stroke-2)
Frame colour (1px) at rest.
--textarea-border-hover
PaintDefault var(--stroke-1)
Frame colour under the pointer.
--textarea-border-focus
PaintDefault var(--ring)
Frame colour while focused.
--textarea-border-disabled
PaintDefault var(--stroke-4)
Frame colour when disabled.
--textarea-halo
PaintDefault var(--fill-3)
The halo (--halo-width) around a focused frame.

--textarea-lh and --textarea-py also set a row's height for minRows and maxRows. Like Input, each state reads its own knob, and invalid always shows the danger frame.

A native <textarea>. Inside Field, the label, the hint and the error are wired by id (for, aria-describedby) and invalid or an error sets aria-invalid. Without a Field, name it with a <label htmlFor> or aria-label; a placeholder is not a label.

Pass required and say so in the label. Errors appear as they are set, so move focus back to the field on a bad submit to have the message read. In a native textarea Enter is a new line; a composer that sends on Enter should say so nearby and keep Shift+Enter for new lines.

Enter
A new line (a composer may send instead)
⇧⏎
A new line in a composer

Textarea

size
"sm" | "md" | "lg"Default "md"
sm and md are UI text (13/18); lg is conversation text (15/24), as in the composer.
variant
"default" | "ghost"Default "default"
ghost drops fill and border (a composer inside its own frame).
invalid
boolean
minRows
numberDefault 3
Minimum height in lines. The field grows with its content from there.
maxRows
number
Growth cap in lines; past it the field scrolls. maxHeight wins when both are set.
maxHeight
number | string
Growth cap as a CSS length ("min(420px, 50vh)") or px.
onValueChange
(value: string) => void
Called with the new text on every change (alongside onChange).
render
RenderProp
Replaces the <textarea>.
rootStyle
TextareaStyle
Typed StyleX override: where the textarea sits and its --textarea-* knobs.
className
string
A product's hook class for its own layout rules. Being retired: new code places a textarea with rootStyle; the call sites left are counted by ui.lint (kit-classname) and only go down.
placeholder
string
Placeholder at text-4.
disabled
boolean
Dims the field.

Rest props go to <textarea> props.