Components · Inputs
Textarea
BetaA multi-line field, such as coach notes on a check-in.
- Source
components/ui/textarea.tsx - shadcn/ui Textarea
- Figma: not linked yet (Q13)
Sara sees this with her week 4 check-in.
When to use
- For free text that may run to several lines: check-in feedback, coach notes, a program description, a message to clients.
When not to use
- For a short single value (a name, a weight, a code): use an Input.
- For formatted content with headings, lists or links: use a rich-text editor.
- For a chat-style composer with send on Enter: that is a pattern built on a textarea, with its own keyboard rules.
Anatomy
Only you and other coaches see notes.
- 1. Label: always visible, linked with htmlFor
- 2. Field: grows with its content from a 64px minimum
- 3. Help text or counter (optional): linked with aria-describedby
Variants
Characters left: 280
| Variant | Use for |
|---|---|
| Auto-growing (default) | Most fields. field-sizing-content grows the field as people type, so nothing scrolls inside it. Set max-h-* if it sits in a small panel. |
| Fixed height, with counter | When the text has a hard limit (an SMS, a push message). Add field-sizing-fixed, rows, maxLength and a counter linked with aria-describedby. |
States
Add a reason so the client knows why.
| State | What changes | Token |
|---|---|---|
| Placeholder | Muted text | placeholder:text-muted-foreground |
| Focus-visible | Border and ring | border-ring, ring-ring/50, 3px |
| Disabled | Half opacity, not-allowed cursor. Dim the label too (see Label) | opacity-(--opacity-disabled) |
Invalid (aria-invalid) | Red border and ring, error text below | border-destructive, ring-destructive/20, text-destructive-text |
Usage guidelines
- Use the placeholder for an example of good content ("What went well this week…"), not for instructions people need while typing. Put those in help text.
- Show a counter only when there is a limit, and say what's left: "Characters left: 120".
- Don't block line breaks or submit on Enter in a form field; people expect Enter to add a new line.
Content guidelines
- Labels are nouns: "Check-in feedback", "Program description". Sentence case, no colon.
- Help text says who sees the text or what it is for: "Sara sees this with her week 4 check-in."
- Error messages say how to fix it: "Add a reason so the client knows why."
- Arabic text in a textarea follows the field's direction. Mixed Arabic and English (exercise names) is handled by the browser; set
dir="auto"on fields where clients may write in either language.
Accessibility
| Key | Does |
|---|---|
| Tab | Moves focus to the field |
| Enter | Adds a new line |
| Shift+Tab | Leaves the field backward |
- Renders a native
<textarea>, linked to its label withhtmlFor/id. Announced as "Check-in feedback, edit text, multi-line", followed by the help text. - Errors: set
aria-invalid="true"and link the message witharia-describedby, so it is read with the label. - A counter linked with
aria-describedbyis read on focus. Don't make it a live region; it would speak on every key press. - Use
useId()for the ids, so two fields on one screen never share one.
RTL
- The text, placeholder and caret follow the page direction; help text and errors align to the start.
- The resize handle stays in the bottom corner on the end side (bottom-left in RTL), drawn by the browser.
- The counter in the example uses
text-end, so it sits at the left in RTL.
Tokens used
Read from components/ui/textarea.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --destructive | border-destructive ring-destructive/20 | red-600 | red-600 |
| --input | border-input | neutral-200 | neutral-200 |
| --muted-foreground | text-muted-foreground | neutral-600 | neutral-600 |
| --ring | border-ring ring-ring/50 | brand-600 | orange-500 |
- Opacity
opacity-(--opacity-disabled) - Radius
rounded-md - Shadow
shadow-xs - Type
text-base - Type
text-sm
Props
<Textarea>
Also takes every prop of <textarea>
No props of its own.
Replaces in coach-admin
From the read-only audit of coach-admin: 46 style values, 48 uses.
coach-admin has a shared formComponents/textarea, but most screens style their own (.faq-textarea, .quick-message-modal__textarea, .post-textarea, .textArea-style). All become this component, with the counter pattern above replacing .textarea-widget__counter.
Classes and selectors
textarea7.faq-textarea6.quick-message-modal__textarea5&.textArea-style4.supercoach-ai__footer-textarea-container3.post-textarea3.textarea-widget__counter3.editInputStyle-textarea-no-value2.el-textarea2.challenge-settings-section__terms-textarea2.verification-queue-modal__textarea2input, select, textarea1
Files
src/reusableComponents/formComponents/textarea/textarea.scss7
The inventory lists the first 12 locations of each value, so file counts are a lower bound.
SCSS variables in these styles
$ai-light-gray-bg→ nearest neutral-50 (no semantic token)