Text Area
Multi-line input. Shares the .input-field wrapper, label, caption, and token set with Text Input — only the shell differs (top-aligned trailing icon, native vertical resize). Three sizes (lg Body 1 / md Body 2 / sm Caption 1) sharing a 120px starting shell — Figma's "size" axis is typography density, not container height. Eight states (Default · Hover · Focus · Filled · Success · Warning · Error · Disabled). Values from Figma node 101-18519.
When to use
- Use for multi-line free-text entry (descriptions, comments, notes, bios)
- Always show a visible label above the field — never rely on placeholder alone
- Allow vertical resize so users can expand the field for longer content
- Use the Error state with a helper message when validation fails
- Set a minimum height that comfortably shows 2–3 lines of text
- Use for single-line input — use Text Input instead
- Use placeholder text as the only label — placeholders disappear on focus
- Set a fixed height that cannot grow — allow vertical resize
- Use
resize: bothorresize: horizontal— vertical only - Use without a character limit when the backend has one — show the limit as helper text
DigiLawyer Patterns
No additional product-specific patterns for Text Area. Same rules as Text Input apply — always pair with a visible label.
Anatomy
Text Area shares the .input-field wrapper with Text Input. The shell is .textarea instead of .input, and the trailing icon sits top-aligned.
Helper text
- 1Label —
.input-field-label. Always visible above the field. - 2Wrapper —
.input-field. Shared with Text Input and Upload Media. Carries the state class. - 3Shell —
.textarea(<div>). Contains the native textarea and optional trailing icon. - 4Native textarea — bare
<textarea>. Resizes vertically only. Font weight 400 always. - 5Trailing icon —
.textarea-trailing. Top-aligned inside the shell. Use for actions or status. - 6Caption —
.input-field-caption. Optional helper text or error message below the field. - 7Resize handle — Native browser vertical resize (bottom-right). Set via
resize: verticalin CSS. Removed automatically on disabled state.
Default
The minimal textarea is a .input-field wrapper with three children: .input-field-label, the .textarea shell (containing a native <textarea> plus an optional top-aligned trailing icon), and .input-field-caption. Hover, Focus, and Filled states are driven by native CSS — no JS, no extra classes in production.
Caption
<div class="input-field"> <label class="input-field-label" for="t1">Label</label> <div class="textarea"> <textarea id="t1" placeholder="Placeholder"></textarea> <span class="textarea-trailing"><svg>…</svg></span> </div> <p class="input-field-caption">Caption</p> </div>
Variants
Two structural variants. With trailing icon is the canonical Figma pattern (helper affordance like "expand", "format", or "attach"). Without trailing icon drops the slot entirely — useful for a clean comment box. The shell handles either case automatically — just omit the .textarea-trailing element. The native resize: vertical handle is always available at the bottom-right; pass style="resize:none" on the <textarea> to lock the height.
Caption
Caption
Caption
Sizes
Three sizes per Figma — all share the 120px starting shell, but typography and the trailing icon scale with the size. .textarea-lg uses Body 1 (16/24) text and a 24×24 trailing icon with 12px symmetric padding. Default md uses Body 2 (14/24) and a 16×16 icon with 8/12 padding. .textarea-sm uses Caption 1 (12/16) and the same 16×16 icon. The native vertical resize lets the shell grow past 120px on user drag.
States
Eight states, same set as Text Input. Hover / Focus / Filled are native CSS (:hover, :focus-within, :has(textarea:not(:placeholder-shown))). Success / Warning / Error change only the caption colour — the textarea chrome stays neutral. Disabled propagates from the native disabled attribute via :has() and also locks resize. .is-hover / .is-focus / .is-filled force visual states at rest for documentation.
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Full matrix
3 sizes × 8 states = 24 cells, mirroring Figma 1:1.
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Caption
Accessibility
Built on a native <textarea> — all standard keyboard and screen reader behaviour is implicit.
| Attribute / Behaviour | Value | Notes |
|---|---|---|
| Label association | <label for="id"> | Always associate the visible label with the textarea via for / id. Never use placeholder as the only label. |
| Keyboard — focus | Tab / Shift+Tab | Moves focus into and out of the textarea |
| Keyboard — new line | Enter | Inserts a line break inside the textarea (does not submit the form) |
| Error state | aria-invalid="true" | Set when validation fails. Pair with aria-describedby pointing to the caption error message. |
| Helper message | aria-describedby="caption-id" | Links the textarea to its caption so screen readers announce it on focus |
| Character limit | maxlength + helper text | Always show the limit visually in the caption — maxlength alone silently truncates |
| Disabled | disabled HTML boolean | Removes from tab order and blocks all interaction |
API
| Class / attr | On | Description |
|---|---|---|
| .input-field | <div> | Shared wrapper with Text Input. Column flex with 4px gap between label / shell / caption. |
| .input-field-label | <label> | Above the shell. Pair its for attribute with the textarea's id. |
| .input-field-caption | <p> | Below the shell. Carries success/warning/error colour via the wrapper state modifier. |
| .textarea | <div> | The shell — position: relative block, min-height: 120px, 8px radius. Holds the native <textarea> (full-width, native vertical resize) and an absolutely-positioned .textarea-trailing icon at top-right. Handles Hover / Focus / Filled via native pseudo-classes. |
| .textarea-lg | .textarea | Large — placeholder/typed text uses Body 1 (16/24), padding is 12px on all sides, trailing icon scales to 24×24. |
| (no size class) | .textarea | Medium (default) — Body 2 (14/24) text, 8/12 padding, 16×16 trailing icon. |
| .textarea-sm | .textarea | Small — Caption 1 (12/16) text, same 8/12 padding and 16×16 icon as Medium. |
| .textarea-trailing | <span> | Absolutely-positioned top-right icon slot. Auto-scales (16×16 default, 24×24 inside .textarea-lg). |
| resize="vertical" / "none" | <textarea> | Native attribute. The shell defaults to resize: vertical on its child textarea — drop in style="resize:none" to lock the height. Disabled textareas force resize: none. |
| .input-field-success | .input-field | Caption colour shifts to --input-success-caption (green). |
| .input-field-warning | .input-field | Caption colour shifts to --input-warning-caption (yellow). |
| .input-field-error | .input-field | Caption colour shifts to --input-error-caption (red). |
| .input-field-disabled / disabled | .input-field / <textarea> | Sets the disabled visual. Prefer the native HTML disabled attribute — the shell adapts via :has() and resize is locked. |
| .is-hover / .is-focus / .is-filled | .textarea | Documentation helpers — force the corresponding state at rest for the Full matrix. Not needed in production code. |