KDS
Forms

Textarea

Campo de texto multi-linha com auto-resize via field-sizing CSS.

Overview

Textarea é a versão multi-linha do Input. Internamente usa field-sizing: content (CSS moderno) — o campo cresce e diminui conforme o conteúdo, sem JavaScript de medição. min-h-16 reserva altura inicial e previne layout shift quando a página carrega (ver [[ui/layout/cumulative-layout-shift]]).

Para entradas de uma linha, prefira Input. Para edição rica (markdown, código, formatação), considere um editor dedicado — Textarea trata só texto plano.

Preview
Textarea com auto-resize.

Anatomy

<Textarea
  ├─ [data-slot="textarea"]
  ├─ field-sizing: content (auto-grow)
  └─ min-h-16 (altura inicial estável)
/>

Usage

import { Textarea } from "@kalvner/kds/forms/textarea";
import { Label } from "@kalvner/kds/forms/label";

export function BioField() {
  return (
    <div className="grid gap-2">
      <Label htmlFor="bio">Bio</Label>
      <Textarea id="bio" placeholder="Conte um pouco sobre você" />
    </div>
  );
}

Props

PropTipoDefaultDescrição
disabledbooleanfalseBloqueia interação.
readOnlybooleanfalsePermite seleção/cópia mas bloqueia edição.
placeholderstringHint dentro do campo.
rowsnumberLinhas iniciais. Compete com min-h-16 — prefira não setar.
aria-invalidbooleanAciona ring vermelho.
...restReact.ComponentProps<'textarea'>Tudo de <textarea>.

States

Default
Altura mínima preservada.
Disabled
Sem pointer events, opacidade reduzida.
Readonly
Selecionável, não editável.
Auto-resize
field-sizing: content cresce com o conteúdo.
Error
aria-invalid + mensagem abaixo.
Bio precisa ter pelo menos 20 caracteres.
Character count
Contador à direita, em texto pequeno.

51 / 280

Composition

Comment box
Caixa de comentário com helper, contador e submit.
Markdown habilitado0 / 500

When to use

  • Mensagens de erro, comentários, descrições, biografias, qualquer texto livre de mais de uma linha.
  • Conteúdo curto-a-médio — algumas frases, talvez um parágrafo.
  • Onde Markdown puro é aceitável e edição rica não agrega.

When not to use

  • Para edição rica (formatação, links, embeds), use um editor dedicado (TipTap, Slate, ProseMirror).
  • Para entrada de uma linha, sempre Input — Textarea de uma linha desperdiça espaço vertical e quebra layout.
  • Para listas estruturadas (tags, checklists), prefira componentes específicos.

Best practices

  • Não set rows. A altura mínima vem do CSS (min-h-16); o crescimento vem do field-sizing. Setar rows força altura fixa e perde o benefício do auto-resize.
  • Contador de caracteres à direita, em text-xs. Informativo, não competitivo com o input. Atualize só após primeiro onInput para evitar flash de "0/500" inicial.
  • Helper e contador na mesma linha quando possível — cabem em flex justify-between, otimizando espaço vertical.
  • Não anime height na transição idle → focused. Causa CLS. Use border-color e box-shadow em vez disso — [[ui/layout/cumulative-layout-shift]].

Accessibility

ConcernComportamento
LabelSempre vincule via htmlFor + id.
Erroaria-invalid + aria-describedby apontando para a mensagem.
Contador como descriçãoUse aria-describedby apontando para o contador para que leitores de tela informem o limite.
Resize manualPor padrão, <textarea> permite resize via canto inferior direito. field-sizing: content desabilita esse comportamento — ok para a maioria.
Touch targetMin-height 64px é confortável para mobile.
  • Input — entrada de uma linha.
  • Form — wrappers de RHF + Zod com Textarea.
  • Label — par obrigatório.

On this page