Multiline text entry. Same contract as the input field — the deltas are multiline metrics: relaxed line height and a three-control minimum height. The control is not user-resizable: the native drag handle can pull it out of its container, and the need it answers belongs to auto-grow.
The state vocabulary is Input's; only the content wraps.
| Example | State | Driver | Roles | Usage |
|---|---|---|---|---|
| Rest | — | border.defaulttext.muted | Placeholder is never essential content | |
| Rest, filled | — | text.primary | Content wraps; the box holds its height | |
| Hover | :hover | border.hover | The border answers the pointer | |
| Focus | :focus-visible | focus.ring | The engaged control — the border takes the focus color, no offset ring | |
| Invalid | [aria-invalid] | status.error.border.fg | Pair with an error message via aria-describedby | |
| Read-only | [readonly] | surface.canvas | Generated content: copyable, not editable | |
| Disabled | :disabled | action.disabled.bg.text | Excluded from interaction and the tab order |
Content past the height scrolls inside the box. Size the control per project for the content you expect — size.control.md × 3 is only the floor.
A textarea is Input, given room — the deltas are metric, not behavioral.
| Verdict | Rule |
|---|---|
| Do | Apply Input's rules unchanged — the label via FormField, the placeholder as a hint, aria-invalid paired with a message via aria-describedby. |
| Do | Reserve textarea for genuinely multiline content — a generous-feeling single-line answer belongs in an input. |
| Do | Size the control for the content you expect — min-height (size.control.md × 3) is a floor, not a ceiling, and content scrolls past it. |
| Don’t | Give the resize handle back per instance — dragging pulls the control out of designed layouts, and the need it serves belongs to auto-grow. |