Form

Textarea

Textarea captures multiple lines of free-text content.

Components · Form

Textarea is input's multi-line sibling — the same label, container, and help-text parts, with a <textarea> in place of the field and a small nm-input--textarea modifier that top-aligns the content and allows vertical resizing.

Visible to everyone on this document.

Overview

The Textarea component is designed for entering free-form text that spans multiple lines. It provides enough space for users to write descriptions, comments, notes, or other longer content while supporting the same labeling, help text, and validation patterns as a standard input.

When to use

Use the Textarea component when:

  • Users need to enter messages, comments, or notes of varying length.
  • The expected input consists of one or more sentences, such as descriptions or explanations, rather than a single value like a name or email address. For single-line values, use an Input.
  • Users need to add free-form information to a record, for example internal comments, signing instructions, or additional context.
  • The content doesn't need document-level formatting such as headings, images, or tables — for that, use a Rich Text Editor. If it only needs light emphasis (bold, italic, underline, lists, links), add the optional formatting toolbar instead of reaching for a full editor.
  • The input is intended for longer text, not short fixed-length values such as verification codes or PINs. For verification codes or PINs, use a Code Field.

Anatomy

Textarea reuses .nm-input__label, .nm-input__container, and .nm-input__help from input without modification. The only new pieces are the nm-input--textarea modifier on the wrapper and a <textarea class="nm-input__field"> instead of <input>.

Tokens and values that drive each part

PartRoleSource
Label / help / container fill / borderSame as input--nm-input-* (see Input)
Container alignmentTop-aligned instead of centered.nm-input--textarea .nm-input__container { align-items: flex-start }
Field line heightRoomier for multi-line reading.nm-input--textarea .nm-input__field { line-height: 1.5 }
Field resizeUser-resizable height only.nm-input--textarea .nm-input__field { resize: vertical }

Sizes

Textarea uses the same five input sizes for its label, border radius, and padding; height instead comes from the rows attribute on the <textarea> itself, not from the size modifier.

xs
inline comments
sm
default for forms
md
standard content forms
lg
prominent single-field forms
xl
marketing, feedback forms

Choosing a row count

Set rows to the number of lines you expect the average answer to take — 2–3 for a short note, 5–6 for an open-ended description. Let the user grow it further with the resize handle rather than guessing high and leaving empty space.

Interaction & validation states

Textarea shares every state with input: hover, focus, read-only, and the three validation intents.

Interaction states

No disabled state. As with every Namirial field, use nm-input--readonly instead of the native disabled attribute — the content stays legible and still submits with the form.

Validation states

Tell the signer why the document was rejected.
This note will be visible to everyone with access.
Looks good — nothing left to change.

Error · warning · success — nm-input--error, nm-input--warning, nm-input--success

Resize behavior

resize: vertical lets the user drag the bottom-right corner to grow the field, without letting it stretch wider than its container and break the surrounding layout. Never set resize: none unless the field's height is already generous enough for the longest realistic answer.

Toolbar

Textarea can surface a lightweight formatting toolbar above the field for content that benefits from basic emphasis, without the overhead of a full Rich Text Editor. Nest a .nm-input__toolbar row before the <textarea> and add nm-input__container--toolbar to the container — it stacks the toolbar above the field and clips both to the container's rounded corners.

  • The toolbar row is presentational scaffolding here — wire each button to your editing library's formatting commands (document.execCommand is deprecated; prefer a library-native command API) and toggle aria-pressed on .nm-input__toolbar-btn to reflect active formatting at the cursor.
  • The Normal control opens a block-style menu (paragraph, heading levels, quote); keep it a <button aria-haspopup="listbox"> wired to your dropdown/menu component, not a native <select>, so its width can hug the label.
  • Keyboard users still need the standard shortcuts (Cmd/Ctrl+B, +I, +U) — the toolbar buttons should mirror those, not replace them.

Light & dark theme

Light data-theme="light"
Dark data-theme="dark"

Both previews are locked — they don't follow the page theme switcher.

Do & don't

✓Do
Size rows to the expected answer length and let resize handle the rest.
✕Don't
Don't force multi-sentence content into a single-line input — the user loses sight of what they wrote.
✓Do
Leave resize on so a user with a longer answer than you expected isn't stuck scrolling inside a tiny box.
✕Don't
Don't disable resize on a field that might realistically overflow.

Accessibility

Semantics

  • Associate the label the same way as input — for/id or nesting inside .nm-input__label.
  • If you enforce a character limit, expose the remaining count in a live region, not just visually.

Keyboard

  • Tab moves focus in and out; the resize handle is native and keyboard-inaccessible by design — don't make resizing the only way to see full content, ensure overflow: auto handles scrolling too.
  • Enter inserts a newline (unlike input, where Enter often submits a form) — never intercept Enter to submit a form containing a textarea unless the user expects that.

Labeling

  • Use aria-describedby to connect help text and any character-count indicator to the field.
  • On error, set aria-invalid="true" alongside the nm-input--error class.

Playground

Visible to everyone on this document.
<div class="nm-input nm-input--textarea nm-input--md"> <label class="nm-input__label">Message</label> <div class="nm-input__container"> <textarea class="nm-input__field" rows="4"></textarea> </div> <span class="nm-input__help">Visible to everyone on this document.</span> </div>

Token reference

Textarea inherits every --nm-input-* token from Input — there is no separate token group. A handful of rules extend the base system:

RuleRole
.nm-input--textarea .nm-input__containerTop-aligns content instead of centering it vertically
.nm-input--textarea .nm-input__fieldSets line-height: 1.5 and resize: vertical
.nm-input__container--toolbarStacks an optional toolbar above the field instead of the default inline layout, and clips both to the container's corners
.nm-input__toolbarRow that holds the block-style control and formatting buttons, separated from the field by a border
.nm-input__toolbar-selectThe "Normal" block-style control
.nm-input__toolbar-btnA single formatting button; use aria-pressed to show active state
.nm-input__toolbar-dividerVertical rule that groups related buttons

Implementation

<link rel="stylesheet" href="namirial-theme.css">

<div class="nm-input nm-input--textarea nm-input--md">
  <label class="nm-input__label">Message</label>
  <div class="nm-input__container">
    <textarea class="nm-input__field" rows="4" placeholder="Add a note for the signer…"></textarea>
  </div>
  <span class="nm-input__help">Visible to everyone on this document.</span>
</div>

With the optional toolbar:

<div class="nm-input nm-input--textarea nm-input--md">
  <label class="nm-input__label">Label<span class="nm-input__required">(required)</span></label>
  <div class="nm-input__container nm-input__container--toolbar">
    <div class="nm-input__toolbar">
      <button type="button" class="nm-input__toolbar-select">Normal<svg><!-- chevron-down --></svg></button>
      <span class="nm-input__toolbar-divider"></span>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Bold"><svg><!-- bold --></svg></button>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Italic"><svg><!-- italic --></svg></button>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Underline"><svg><!-- underline --></svg></button>
      <span class="nm-input__toolbar-divider"></span>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Numbered list"><svg><!-- list-ordered --></svg></button>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Bulleted list"><svg><!-- list --></svg></button>
      <span class="nm-input__toolbar-divider"></span>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Insert link"><svg><!-- link --></svg></button>
      <span class="nm-input__toolbar-divider"></span>
      <button type="button" class="nm-input__toolbar-btn" aria-label="Clear formatting"><svg><!-- remove-formatting --></svg></button>
    </div>
    <textarea class="nm-input__field" rows="5" placeholder="Textarea placeholder"></textarea>
  </div>
</div>

CSS classes

ClassValuesDefault
nm-input--textareaSwitches container alignment and field line-height/resize for multi-line content—
nm-input--{size}xs · sm · md · lg · xlsm
nm-input--{intent}error · warning · success—
nm-input--readonlyLocks the container against edits—
nm-input__container--toolbarAdd to .nm-input__container to stack an optional formatting toolbar above the field—