Content
- Placeholder: a short example of the kind of thing to write, like “Met at the Saltmarsh dinner. Interested in climate deals.” Never the label.
- Help text says who sees it: “Only you can see notes.”
A text field for more than one line: notes, bios, messages.
import { … } from "@brdgapp/ui/components/textarea"Textarea is a text field for more than one line: a note, a bio, an intro message. It starts 3 lines tall, grows as people type up to 10 lines, then scrolls inside. Give it a maxLength and it counts the characters under the box. Put it in a Field, like Input. The usual mistake is a fixed little box that hides most of what someone wrote, or a count each screen builds its own way.
maxLength wherever the server has a limit, so people see it coming instead of being cut off.The look comes from props
A component's appearance comes only from its own options (variant, size, color). className on a package component is for layout (mt-4, shrink-0, w-full), never for font size, color, padding, radius or hover. If the look needs a className, the component or the chosen option is wrong: fix that instead.
223 of 280 characters left
Box. The same box as Input: white --surface-primary, --border-default line, 8px corners, 14px side padding. 3 lines (82px) tall when empty.
Text. 14px --text-primary on a 20px line; 16px on phones, so iPhones don't zoom in when it's tapped. The placeholder is --text-muted.
Count. Only with maxLength. 12px, under the box on the right: “120/500”. Turns red in the last 10% (at most the last 50 characters).
Type more than 3 lines and the box grows. The count under it shows how much is left.
Already-written text shows in full, up to 10 lines, then the box scrolls.
In the last 10% the count turns red, and screen readers hear how many characters are left.
179 of 280 characters left
16 of 280 characters left
16 characters leftUnder the field, with an icon, saying what to write.
| Rest | Hover | Focus | Disabled | |
|---|---|---|---|---|
| Empty | ||||
| Filled | ||||
| Invalid |
| State | What happens |
|---|---|
| Rest | Grey line. |
| Hover | The line darkens to --border-hover. |
| Focus | Blue line and a soft blue ring. |
| Invalid | Red line (and a soft red ring on focus). The error text sits under it, in the Field. |
| Near the limit | The count turns red; screen readers hear how many characters are left. |
| Disabled | 50% opacity; can't be typed in. |
rows (3) to maxRows (10), then scrolls inside. Set maxRows equal to rows for a fixed height.maxLength stops typing at the limit and shows the count.className is for layout only (max-w-md). Never a height: use rows.Do
Let it grow, and setmaxLength to the server's limit.Don't
Fix it at 2 lines with a hidden scrollbar, so people can't see what they wrote.Do
Put it in a Field with a visible label: “Note”.Don't
Use the placeholder as the label: “Write a note…”.aria-label.--danger-text, 5.5:1 in light and 5.8:1 in dark.| Keys | Action |
|---|---|
| Enter | Starts a new line. It never sends: the form's button does. |
| Tab | Moves to the next field. |
Textarea
The multi-line field. Extends Native textarea props (value, defaultValue, onChange, onBlur, placeholder, disabled, name…).
| Prop | Type | Default | Description |
|---|---|---|---|
rows | number | 3 | Lines tall when empty. |
maxRows | number | 10 | Lines it grows to before scrolling. Equal to rows for a fixed height. |
maxLength | number | — | The character limit. Shows the count under the box. |
aria-invalid | boolean | — | Only outside a Field. Inside one, set invalid on the Field. |
className | string | — | Layout only: max-w-md. |
What it's built from and where it's used, read from the code. When you change it, check these too.