Textarea
Textareas permitem que usuários insiram texto multilinha. Usa um padrão de wrapper similar ao componente Input.Textareas let users enter multi-line text. Uses a wrapper pattern similar to the Input component.
Quando usarWhen to use
AnatomiaAnatomy
.ds-textarea) -- mesmo padrão de borda/radius do Input.2 Campo (
.ds-textarea__field) -- o elemento nativo <textarea>.3 Alça de redimensionamento -- nativa do navegador, apenas vertical por padrão.
4 Label (
.ds-field__label) -- externo, via Form Field.5 Mensagem de erro (
.ds-field__error) -- externa, via Form Field.6 Texto auxiliar (
.ds-field__helper) -- externo, via Form Field.Contador de caracteres (opcional,
.ds-field__counter) -- disponível via wrapper Form Field.1 Wrapper (.ds-textarea) -- same border/radius pattern as Input.2 Field (
.ds-textarea__field) -- the native <textarea> element.3 Resize handle -- browser-native, vertical only by default.
4 Label (
.ds-field__label) -- external, via Form Field.5 Error message (
.ds-field__error) -- external, via Form Field.6 Helper text (
.ds-field__helper) -- external, via Form Field.Character counter (optional,
.ds-field__counter) -- available via Form Field wrapper.
PadrãoDefault
<div class="ds-field">
<div class="ds-field__label-row">
<label class="ds-field__label" for="textarea-default">Label</label>
</div>
<div class="ds-textarea">
<textarea class="ds-textarea__field" id="textarea-default"
placeholder="Enter your message..."
aria-describedby="textarea-default-helper"></textarea>
</div>
<span class="ds-field__helper" id="textarea-default-helper">Helper text</span>
</div>
TamanhosSizes
<div class="ds-textarea ds-textarea--sm">
<textarea class="ds-textarea__field" placeholder="Small"></textarea>
</div>
<div class="ds-textarea ds-textarea--md">
<textarea class="ds-textarea__field" placeholder="Medium"></textarea>
</div>
<div class="ds-textarea ds-textarea--lg">
<textarea class="ds-textarea__field" placeholder="Large"></textarea>
</div>
| Size | Min-height | Caso de usoUse case |
|---|---|---|
Small (--sm) | 80px | Entradas curtas: avaliações, comentários brevesShort inputs: feedback ratings, brief comments |
Medium (--md) | 120px | Uso padrão: mensagens, descrições (default)Standard use: messages, descriptions (default) |
Large (--lg) | 160px | Conteúdo longo: bios, feedback detalhadoLong-form content: bios, detailed feedback |
Contador de caracteresCharacter Counter
<div class="ds-field">
<label class="ds-field__label" for="bio">Bio</label>
<div class="ds-textarea">
<textarea class="ds-textarea__field" id="bio"
data-maxlength="200" data-counter="bio-counter"></textarea>
</div>
<span class="ds-field__counter" id="bio-counter"
aria-live="polite">0 / 200</span>
</div>
EstadosStates
No Figma, State representa apenas estados visuais exclusivos: Default, Hover, Focus e Disabled. Filled, Error e Read-only são propriedades separadas; Error combina com Default/Hover/Focus, e Read-only combina com Default/Focus.In Figma, State only represents exclusive visual states: Default, Hover, Focus, and Disabled. Filled, Error, and Read-only are separate properties; Error combines with Default/Hover/Focus, and Read-only combines with Default/Focus.
Error
<div class="ds-field ds-field--error">
<label class="ds-field__label" for="msg">Message</label>
<div class="ds-textarea ds-textarea--error">
<textarea class="ds-textarea__field" id="msg"
aria-invalid="true" aria-describedby="msg-error"></textarea>
</div>
<span class="ds-field__error" id="msg-error">Message must be at least 20 characters.</span>
<span class="ds-field__helper">Write a clear message with enough detail.</span>
</div>
Disabled
<div class="ds-textarea">
<textarea class="ds-textarea__field" disabled>Disabled textarea</textarea>
</div>
Readonly
<div class="ds-textarea">
<textarea class="ds-textarea__field" readonly>Read-only content</textarea>
</div>
| Figma property | Value / CSS trigger | Visual change | Token |
|---|---|---|---|
| State | Default | Neutral surface and border | component.field.bg.default, component.field.border-color.default |
| State | Hover / :hover | Darker border | component.field.border-color.hover |
| State | Focus / :focus-within | 2px outline ring, component radius | component.field.border-color.focus, component.focus-ring.*, component.textarea.focus-ring.radius.default |
| State | Disabled / [disabled] or .ds-textarea--disabled | Muted bg, border and content | component.field.bg.disabled, component.field.value.color.disabled |
| Error | True / .ds-textarea--error or .ds-field--error .ds-textarea | Red border and error focus ring | component.field.border-color.error, component.focus-ring.color.error |
| Error + State | True + Hover / .ds-textarea--error:hover | Darker red border | component.field.border-color.error-hover |
| Read-only | True / [readonly] or .ds-textarea--readonly | Readonly bg with neutral focus ring | component.field.bg.readonly, component.focus-ring.color.readonly |
Boas práticasBest practices
Diretrizes de conteúdoContent guidelines
| RegraRule | ExemploExample |
|---|---|
| Labels seguem as mesmas regras do InputLabels follow the same rules as Input | "Message", "Description", "Bio" -- not "Enter your message here" |
| Placeholders podem ser um pouco mais longos (campo mais largo)Placeholders can be slightly longer (wider field) | "Tell us about your experience..." -- not just "Type here" |
| Mensagens de erro descrevem o que deu errado e como corrigirError messages describe what went wrong and how to fix | "Message must be at least 20 characters." -- not "Error" |
| Formato do contador: atual / máximoCharacter counter format: current / max | "42 / 200" -- not "42 characters" or "158 remaining" |
Contador acima do limite usa ds-field__counter--overOver-limit counter uses ds-field__counter--over | Contador fica vermelho quando atual > máximoCounter turns red when current > max |
Mapeamento de tokensToken mapping
Textarea consome os contratos compartilhados de Field/Form Field para superfície, estados e mensagens, e mantém component.textarea.* apenas para contratos próprios como padding-x, tipografia do valor e min-height. O contador continua CSS-only porque não existe no component set vivo do Figma.Textarea consumes shared Field/Form Field contracts for surface, states, and messages, and keeps component.textarea.* only for owned contracts such as padding-x, value typography, and min-height. The counter remains CSS-only because it does not exist in the live Figma component set.
| PropriedadeProperty | Token | Variável CSSCSS variable |
|---|---|---|
| background | component.field.bg.* | --ds-field-bg-* |
| border-color | component.field.border-color.* | --ds-field-border-color-* |
| border-width | component.field.border-width | --ds-field-border-width |
| radius | component.field.radius | --ds-field-radius |
| focus ring | component.focus-ring.* + component.textarea.focus-ring.radius.default | --ds-focus-ring-* + --ds-textarea-focus-ring-radius-default |
| content color | component.field.value.color.{default,disabled,readonly} | --ds-field-value-color-* |
| placeholder color | component.field.placeholder.color.{default,disabled} | --ds-field-placeholder-color-* |
| padding-x (sm/md/lg) | component.textarea.padding-x.{sm,md,lg} | --ds-textarea-padding-x-* |
| padding-y (sm/md/lg) | component.field.padding-y.{sm,md,lg} | --ds-field-padding-y-* |
| text typography | component.textarea.text.{font-family,font-size,font-weight,letter-spacing,line-height}.* | --ds-textarea-text-* |
| field min-height (sm) | component.textarea.field.min-height.sm | --ds-textarea-field-min-height-sm (80px) |
| field min-height (md) | component.textarea.field.min-height.md | --ds-textarea-field-min-height-md (96px) |
| field min-height (lg) | component.textarea.field.min-height.lg | --ds-textarea-field-min-height-lg (120px) |
| label/helper/required | component.form-field.{label,helper,required}.* | --ds-form-field-label-*, --ds-form-field-helper-*, --ds-form-field-required-* |
| stack/label-row gap | component.form-field.stack.gap.default, component.form-field.label-row.gap.default | --ds-form-field-stack-gap-default, --ds-form-field-label-row-gap-default |
| counter color | semantic.content.default | --ds-content-default |
| counter color (over) | semantic.feedback.error.content-default | --ds-feedback-error-content-default |
Nota: Textarea não usa component.*.height como Button/Input/Select. O contrato anatômico principal é component.textarea.field.min-height.*, aplicado ao field visual multilinha.Note: Textarea does not use component.*.height like Button/Input/Select. Its main anatomical contract is component.textarea.field.min-height.*, applied to the multiline visual field.
Classes CSSCSS classes
| ClasseClass | DescriçãoDescription |
|---|---|
ds-textarea | Elemento wrapper do textareaWrapper element for the textarea |
ds-textarea__field | O elemento nativo <textarea>The native <textarea> element |
ds-textarea--sm | Tamanho pequeno (80px min-height)Small size (80px min-height) |
ds-textarea--md | Tamanho médio (96px min-height, padrão)Medium size (96px min-height, default) |
ds-textarea--lg | Tamanho grande (120px min-height)Large size (120px min-height) |
ds-textarea--error | Estado de erro com borda vermelhaError state with red border |
ds-textarea--disabled | Estado desabilitado (ou use o nativo disabled)Disabled state (or use native disabled) |
ds-textarea--readonly | Estado somente leitura (ou use o nativo readonly)Readonly state (or use native readonly) |
ds-field__counter | Elemento contador de caracteresCharacter counter element |
ds-field__counter--over | Aplicado quando contagem de caracteres excede o máximoApplied when character count exceeds max |
ds-field__error | Linha de erro com ícone automático e mensagem (exibida quando ds-field--error está definido)Error row with automatic icon and message (shown when ds-field--error is set) |
ds-field__label-row | Linha horizontal com label e asterisco de obrigatórioHorizontal row with label and required asterisk |
ds-field__required | Asterisco * em feedback/error/content/default (decorativo, aria-hidden)Asterisk * in feedback/error/content/default (decorative, aria-hidden) |
ds-field--no-label | Oculta o label row quando Show Label = falseHides the label row when Show Label = false |
ds-field--no-helper | Oculta o helper text quando Show Helper Text = falseHides helper text when Show Helper Text = false |
Propriedades FigmaFigma properties
| PropriedadeProperty | TipoType | PadrãoDefault | DescriçãoDescription |
|---|---|---|---|
Show Label | Boolean | true | Exibe ou oculta o label row (incluindo asterisco de obrigatório)Shows or hides the label row (including required asterisk) |
Label | Text | "Rótulo" | Texto do label (label/md)Label text (label/md) |
Required | Boolean | false | Exibe o asterisco * em feedback/error/content/default ao lado do labelShows * in feedback/error/content/default next to the label |
Show Helper Text | Boolean | true | Exibe ou oculta o texto auxiliar abaixo do controleShows or hides the helper text below the control |
Helper Text | Text | "Texto auxiliar" | Anotação em caption/sm (content/secondary)Caption/sm annotation (content/secondary) |
Interação por tecladoKeyboard interaction
| TeclaKey | AçãoAction |
|---|---|
Tab | Move o foco para dentro / fora do textareaMoves focus into / out of the textarea |
Enter | Insere uma nova linha (NÃO envia o formulário)Inserts a new line (does NOT submit the form) |
| Qualquer caractereAny character | Digita no campoTypes into the field |
Diferente de inputs de linha única, pressionar Enter dentro de um textarea cria uma nova linha em vez de enviar o formulário. Este é o comportamento nativo do navegador.Unlike single-line inputs, pressing Enter inside a textarea creates a new line instead of submitting the form. This is native browser behavior.
AccessibilityAccessibility
| Critério WCAGWCAG criterion | RequisitoRequirement | Status |
|---|---|---|
| 1.3.1 Info and Relationships (A) | Every textarea must have a visible <label> with matching for/id. | ✓ |
| 1.3.5 Identify Input Purpose (AA) | Use autocomplete attributes where applicable (e.g. autocomplete="street-address"). | ✓ |
| 1.4.3 Contrast (AA) | Text and placeholder meet 4.5:1 and 3:1 contrast respectively. | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring 2px + 2px gap, contrast ≥ 3:1 against adjacent colors. | ✓ |
| 3.3.1 Error Identification (A) | Error state uses aria-invalid="true" + aria-describedby linking to the error message. | ✓ |
| 3.3.2 Labels or Instructions (A) | Placeholder is supplemental, never a replacement for the label. | ✓ |
| 4.1.3 Status Messages (AA) | Character counter uses aria-live="polite" so screen readers announce updates. | ✓ |
aria-invalid="true" -- definido quando o campo tem erro de validação.aria-describedby -- referencia o ID do elemento de mensagem de erro ou texto auxiliar.aria-live="polite" -- no contador de caracteres para atualizações dinâmicas de leitores de tela.Se
maxlength estiver definido no elemento nativo, navegadores impõem o limite -- sem necessidade de validação JS.aria-invalid="true" -- set when the field has a validation error.aria-describedby -- references the error message or helper text element ID.aria-live="polite" -- on the character counter for dynamic screen reader updates.If
maxlength is set on the native element, browsers enforce the limit -- no JS validation needed.
Required = true, adicione aria-required="true" no <textarea> — o asterisco visual (.ds-field__required) é decorativo (aria-hidden="true"). Quando Show Label = false, use aria-label no <textarea>. O Helper Text deve ser vinculado via aria-describedby.When Required = true, add aria-required="true" to the <textarea> — the visual asterisk (.ds-field__required) is decorative (aria-hidden="true"). When Show Label = false, use aria-label on the <textarea>. Helper Text should be linked via aria-describedby.