Zetta v1.6

Components · Forms & selection

Textarea

Multi-line text input supporting resizable behavior. Split from zetta-form-v1_4.md (removed in v1.5) in v1.4.

Preview

A short description for your profile. Grows as you type.

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Textarea" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `textarea` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `textarea` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

┌─────────────────────────────────────────────────┐
│  Placeholder or entered text                     │  ← min-height 80px
│                                                  │
│                                                  │
└─────────────────────────────────────────────────╗┘
                                                  ↑ resize handle (vertical)

Parts:

  • Container — full-width, {rounded.base} radius, {colors.border-strong} border at rest
  • Text content — 14px / 400 Geist, {colors.ink}
  • Placeholder{colors.placeholder}, italic
  • Resize handle — browser-native, visible at bottom-right when resize: vertical (default)
State Border Fill Resize
Default {colors.border-strong} {colors.surface-card} vertical
Hover {colors.muted} {colors.surface-card} vertical
Focus {colors.primary} + glow {colors.surface-card} vertical
Error {colors.danger} + glow {colors.surface-card} vertical
Disabled {colors.disabled-border} {colors.disabled-bg} none
Read-only {colors.hairline} {colors.surface-muted} none

When to use

  • Multi-line text entry: descriptions, comments, notes, messages, feedback.
  • Any field where the user's input may span multiple lines.

When not to use

  • Single-line input — use Text Input.
  • Structured multi-field data — use multiple single-line inputs instead.
  • Rich text with formatting — use a rich text editor component.

Do

  • Set a meaningful rows value that reflects the expected content length.
  • Allow vertical resize (default) so users can see more of their content.
  • Show character count when there is a limit.

Don't

  • Don't use resize: none unless the layout genuinely cannot accommodate resizing.
  • Don't use textarea for short single-line answers — use text input.
  • Labels: always visible above the textarea.
  • Placeholder: short hint — "Describe the issue in detail." or leave blank.
  • Character count: show below-right when limit applies — "240 / 500".
  • Error: "Description is required." or "Must be under 500 characters."
  • Avoid using textarea for structured data — use multiple single-line inputs instead.

Keyboard

Key Behavior
Tab Move focus to the textarea
Shift + Tab Move focus away from the textarea
Standard text keys All standard browser text editing behavior
Tab (inside textarea) Inserts a tab character (browser default — override if needed)

Accessibility

  • Always pair with a <label> via for/id.
  • Error state: aria-invalid="true" and aria-describedby.
  • Contrast: {colors.ink} on {colors.surface-card} is 21:1 (AAA).

Spec

Values and token references resolved straight from the Zetta spec.

input-textarea
backgroundColor
{colors.surface-card}
textColor
{colors.ink}
borderColor
{colors.border-strong}
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
400
lineHeight
1.6
minHeight
80px
paddingX
{spacing.sm}
paddingY
{spacing.xs}
width
100%
resize
vertical
transition
border-color {motion.duration-fast}, box-shadow {motion.duration-fast}
input-textarea-placeholder
textColor
{colors.placeholder}
input-textarea-hover
borderColor
{colors.muted}
input-textarea-focus
borderColor
{colors.primary}
outline
none
boxShadow
0 0 0 2px {colors.primary-bg}
input-textarea-disabled
backgroundColor
{colors.disabled-bg}
textColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
cursor
not-allowed
resize
none
input-textarea-error
borderColor
{colors.danger}
boxShadow
0 0 0 2px {colors.danger-bg}
input-textarea-read-only
backgroundColor
{colors.surface-muted}
textColor
{colors.ink}
borderColor
{colors.hairline}
cursor
default
resize
none
input-textarea-resize-none
resize
none
input-textarea-resize-horizontal
resize
horizontal
input-textarea-resize-both
resize
both