Textarea
Takes several lines of text.
Sizing
rowsdefault
autoGrow
States
rest
hover
focus
6 left
Near the character limitnear limit
10 over the 24 limit
Over the 24 character limitover limit
disabled
read-only
Usage
When to use it
- Use
- The answer runs past one line: a run note, a reason for a rollback, a description of a pipeline.
- Use
- The text has a soft length limit the person should see coming (limit).
- Not for
- The answer fits on one line. Use Input.
- Not for
- The text is code, SQL or JSON to edit: use a code editor, which keeps indentation and mono.
Rules
7 rules
- Must
- Put a Textarea in a Field with a FieldLabel. Because a placeholder vanishes on the first keystroke and is not a reliable accessible name.
- Must
- Use limit for a length cap, never maxLength. Because maxLength cuts a paste without a word, while limit keeps every character, counts down near the end and marks the field invalid past it.
- Never
- Round the corners or change the edge of a Textarea. Because every corner in Sodium is square, and the edge is the one that turns gold on focus and red when invalid.
- Never
- Draw a ring, an outline or a shadow to show focus. Because a field shows focus itself, white with the gold edge doubled inward and a 1px pale yellow halo, and a ring outside that reads as a second box.
- Should
- With autoGrow, set maxRows so a long paste scrolls inside the box instead of pushing the form off the screen.
- Should
- Leave resizable off when autoGrow is on; the box already fits its text. Because a dragged height fights the grown one on the next keystroke.
- Should
- Make the placeholder an example of a good answer, not the label again.
State order
8 states, highest first
- 1
- disabled
- 2
- invalid
- 3
- over-limit
- 4
- focus
- 5
- near-limit
- 6
- read-only
- 7
- hover
- 8
- rest
Accessibility
4 checks
- A native textarea named by FieldLabel, with aria-invalid and aria-describedby from the Field.
- With limit, the counter joins aria-describedby, and a status region announces only crossing 80% and the limit, never every keystroke.
- Focus turns the edge --focus at 2px beside the caret; invalid adds the error sentence, so neither rests on colour alone.
- Text is 16px under a coarse pointer, so a phone browser never zooms on focus.
Keyboard
2 keys
- Enter
- Adds a line; it never submits the form.
- Tab
- Leaves the textarea; it never types a tab.
Edge cases
6 edge cases
- Paste over the limit
- Keeps every character, counts how far over in the failure word and marks the field invalid.
- Long paste with autoGrow
- Grows to maxRows, then scrolls inside the box.
- Browser without field-sizing
- autoGrow measures scrollHeight on every input instead.
- Narrow container
- The width follows the container; only the height follows the text.
- Right to left
- Text and the counter align to the reading direction.
- Dark theme
- The body is #181715 and lifts a step under the pointer; focus turns it the page with a yellow edge.
API
7 props
rowsnumberdefault3autoGrowbooleandefaultfalseminRows, maxRowsnumberdefault3, 10limitnumberresizablebooleandefaultfalseonValueChange(value: string) => voidaria-invalidboolean
Tokens
22 tokens
--field-bg- #f8f7f3
--field-hover- #fefefd
--field-edge-hover- #ecebe5
--field-ring-hover- #f4f3ee
--field-focus-bg- #ffffff
--field-ring-focus- #fff1b8
--field-ring-fail- #fbdcd8
--field-inset- 10px
--well- #f4f3ee
--line- #e2dfd5
--focus- #7a5d00
--fail- #b3261e
--fail-text- #b3261e
--text-1- #161512
--text-2- #55524a
--text-3- #6b675e
--text-disabled- #a8a499
--type-body-sm- 400 14px/20px var(--font-sans)
--type-label-sm- 400 12px/16px var(--font-mono)
--space-1-5- 6px
--dur-light- 240ms
--ease-soft- cubic-bezier(0.22, 0.8, 0.24, 1)