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.
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
| Part | Role | Source |
|---|---|---|
| Label / help / container fill / border | Same as input | --nm-input-* (see Input) |
| Container alignment | Top-aligned instead of centered | .nm-input--textarea .nm-input__container { align-items: flex-start } |
| Field line height | Roomier for multi-line reading | .nm-input--textarea .nm-input__field { line-height: 1.5 } |
| Field resize | User-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.
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
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.execCommandis deprecated; prefer a library-native command API) and togglearia-pressedon.nm-input__toolbar-btnto reflect active formatting at the cursor. - The
Normalcontrol 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
Both previews are locked — they don't follow the page theme switcher.
Do & don't
rows to the expected answer length and let resize handle the rest.Accessibility
Semantics
- Associate the label the same way as input —
for/idor 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: autohandles 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-describedbyto connect help text and any character-count indicator to the field. - On error, set
aria-invalid="true"alongside thenm-input--errorclass.
Playground
Token reference
Textarea inherits every --nm-input-* token from Input — there is no separate token group. A handful of rules extend the base system:
| Rule | Role |
|---|---|
| .nm-input--textarea .nm-input__container | Top-aligns content instead of centering it vertically |
| .nm-input--textarea .nm-input__field | Sets line-height: 1.5 and resize: vertical |
| .nm-input__container--toolbar | Stacks an optional toolbar above the field instead of the default inline layout, and clips both to the container's corners |
| .nm-input__toolbar | Row that holds the block-style control and formatting buttons, separated from the field by a border |
| .nm-input__toolbar-select | The "Normal" block-style control |
| .nm-input__toolbar-btn | A single formatting button; use aria-pressed to show active state |
| .nm-input__toolbar-divider | Vertical 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
| Class | Values | Default |
|---|---|---|
nm-input--textarea | Switches container alignment and field line-height/resize for multi-line content | — |
nm-input--{size} | xs · sm · md · lg · xl | sm |
nm-input--{intent} | error · warning · success | — |
nm-input--readonly | Locks the container against edits | — |
nm-input__container--toolbar | Add to .nm-input__container to stack an optional formatting toolbar above the field | — |