Textarea

Takes several lines of text.

Sizing

  • rowsdefault

  • autoGrow

States

  • rest

  • hover

  • focus

  • 6 left

    Near the character limit

    near limit

  • 10 over the 24 limit

    Over the 24 character limit

    over 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
rows
number default 3
autoGrow
boolean default false
minRows, maxRows
number default 3, 10
limit
number
resizable
boolean default false
onValueChange
(value: string) => void
aria-invalid
boolean

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)

Install

For your coding agent

GitHub

Please star me pookie

198

A new design system every week

Leave your email before you copy the prompt.