Text Area

Molecule

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

✅ Do
  • 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
❌ Don't
  • 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: both or resize: 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

  1. 1Label.input-field-label. Always visible above the field.
  2. 2Wrapper.input-field. Shared with Text Input and Upload Media. Carries the state class.
  3. 3Shell.textarea (<div>). Contains the native textarea and optional trailing icon.
  4. 4Native textarea — bare <textarea>. Resizes vertically only. Font weight 400 always.
  5. 5Trailing icon.textarea-trailing. Top-aligned inside the shell. Use for actions or status.
  6. 6Caption.input-field-caption. Optional helper text or error message below the field.
  7. 7Resize handle — Native browser vertical resize (bottom-right). Set via resize: vertical in 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

HTML
<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.

Large · Body 1
Medium · Body 2
Small · Caption 1
Default

Caption

Caption

Caption

Hover

Caption

Caption

Caption

Focus

Caption

Caption

Caption

Filled

Caption

Caption

Caption

Success

Caption

Caption

Caption

Warning

Caption

Caption

Caption

Error

Caption

Caption

Caption

Disabled

Caption

Caption

Caption

Accessibility

Built on a native <textarea> — all standard keyboard and screen reader behaviour is implicit.

Attribute / BehaviourValueNotes
Label association<label for="id">Always associate the visible label with the textarea via for / id. Never use placeholder as the only label.
Keyboard — focusTab / Shift+TabMoves focus into and out of the textarea
Keyboard — new lineEnterInserts a line break inside the textarea (does not submit the form)
Error statearia-invalid="true"Set when validation fails. Pair with aria-describedby pointing to the caption error message.
Helper messagearia-describedby="caption-id"Links the textarea to its caption so screen readers announce it on focus
Character limitmaxlength + helper textAlways show the limit visually in the caption — maxlength alone silently truncates
Disableddisabled HTML booleanRemoves from tab order and blocks all interaction

API

Class / attrOnDescription
.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.textareaLarge — placeholder/typed text uses Body 1 (16/24), padding is 12px on all sides, trailing icon scales to 24×24.
(no size class).textareaMedium (default) — Body 2 (14/24) text, 8/12 padding, 16×16 trailing icon.
.textarea-sm.textareaSmall — 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-fieldCaption colour shifts to --input-success-caption (green).
.input-field-warning.input-fieldCaption colour shifts to --input-warning-caption (yellow).
.input-field-error.input-fieldCaption 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.textareaDocumentation helpers — force the corresponding state at rest for the Full matrix. Not needed in production code.