Pular para o conteúdo

Textarea

templates/components/Textarea

É o campo de texto livre, multi-linha e longo — auto-cresce com o conteúdo (field-sizing-content, min-h-16) em vez de rolar como o Input. Por isso suas capacidades giram em torno de texto extenso: contar/limitar comprimento, controlar o crescimento e o redimensionamento, definir a semântica da tecla Enter, proteger o rascunho e pré-visualizar formatação.

Base congelada

Conteúdo

Contador de comprimento

Mostra a contagem viva do texto…

Mostra a contagem viva do texto abaixo do campo — por caracteres, palavras ou caracteres restantes. posicionável

Limite máximo

Define um teto de caracteres; ao…

Define um teto de caracteres; ao atingir, impede digitar além (ou apenas alerta usando o token destructive já existente).

Teto de crescimento

Limita quantas linhas o campo auto-cresce…

Limita quantas linhas o campo auto-cresce antes de passar a rolar internamente, evitando que textos enormes empurrem a página.

Mensagem de ajuda/erro

Texto descritivo de apoio ou erro…

Texto descritivo de apoio ou erro junto ao campo, ligado por aria-describedby; o erro reaproveita o estado aria-invalid da base. posicionável

Rascunho automático

Persiste o que foi digitado (localStorage…

Persiste o que foi digitado (localStorage por campo) e restaura ao reabrir, protegendo textos longos de perda acidental.

Pré-visualização Markdown

Adiciona alternância Editar/Pré-visualizar que renderiza o…

Adiciona alternância Editar/Pré-visualizar que renderiza o Markdown digitado numa aba de leitura, sem alterar o campo de edição. posicionável

Comportamento

Comportamento do Enter

Escolhe a semântica da tecla Enter…

Escolhe a semântica da tecla Enter para caixas de mensagem/comentário: quebrar linha, enviar, ou enviar com Shift+Enter para quebrar.

Redimensionamento

Controla a alça de resize do…

Controla a alça de resize do usuário: travado, só na vertical ou livre — sem tocar no auto-crescer da base.
Código gerado

Cole no Claude Code — ele acerta de primeira.

Textarea (UI) — Design System (Symfony UX Toolkit / shadcn)

BASE CONGELADA (não mude por instância): Congelados e imutáveis: o elemento único <textarea> com data-slot="textarea"; cantos rounded-lg; borda border-input e fundo bg-transparent (dark bg-input/30); padding px-2.5 py-2; tipografia text-base / md:text-sm e cor de placeholder text-muted-foreground; altura mínima min-h-16 e o auto-dimensionamento field-sizing-content; foco (outline-none, focus-visible:border-ring + ring-3 ring-ring/50); estados disabled (cursor-not-allowed, opacity-50, bg-input/50); e o estado de erro via aria-invalid (border-destructive + ring destructive). Nenhuma capacidade altera fonte, cor, cantos ou espaçamento — só somam comportamento ou conteúdo ao redor.

USO: <twig:Textarea />

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Contador de comprimento: Mostra a contagem viva do texto abaixo do campo — por caracteres, palavras ou caracteres restantes. [select → caracteres / palavras / caracteres restantes]- Limite máximo: Define um teto de caracteres; ao atingir, impede digitar além (ou apenas alerta usando o token destructive já existente). [number]- Comportamento do Enter: Escolhe a semântica da tecla Enter para caixas de mensagem/comentário: quebrar linha, enviar, ou enviar com Shift+Enter para quebrar. [select → Enter quebra linha (padrão) / Ctrl/Cmd+Enter envia / Enter envia · Shift+Enter quebra]- Redimensionamento: Controla a alça de resize do usuário: travado, só na vertical ou livre — sem tocar no auto-crescer da base. [select → Travado / Vertical / Livre]- Teto de crescimento: Limita quantas linhas o campo auto-cresce antes de passar a rolar internamente, evitando que textos enormes empurrem a página. [number]- Mensagem de ajuda/erro: Texto descritivo de apoio ou erro junto ao campo, ligado por aria-describedby; o erro reaproveita o estado aria-invalid da base. [text]- Rascunho automático: Persiste o que foi digitado (localStorage por campo) e restaura ao reabrir, protegendo textos longos de perda acidental. [toggle]- Pré-visualização Markdown: Adiciona alternância Editar/Pré-visualizar que renderiza o Markdown digitado numa aba de leitura, sem alterar o campo de edição. [toggle]FAÇA:
- Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.
- Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.
- Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}.
NÃO FAÇA:
- Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.
- Não crie variante/fork para a mesma coisa — existe UMA base por componente.
- Não reimplemente o componente nem adicione toolchain Node.
TESTE ANTES DE MUDAR: "é função ou é base?" — função = capacidade opt-in; base = mude o token na fonte única (assets/styles/app.css), para todos.

A11Y (herdada da base): foco (outline-none, focus-visible:border-ring + ring-3 ring-ring/50). e o estado de erro via aria-invalid (border-destructive + ring destructive)

Via MCP: tool get_component com {"id": "textarea"} · list_capabilities("textarea").

Spec crua: config/ds-specs/textarea.json · Conectar o MCP.

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "textarea",
    "component": "Textarea",
    "eixo": "ui",
    "particularidade": "É o campo de texto livre, multi-linha e longo — auto-cresce com o conteúdo (field-sizing-content, min-h-16) em vez de rolar como o Input. Por isso suas capacidades giram em torno de texto extenso: contar/limitar comprimento, controlar o crescimento e o redimensionamento, definir a semântica da tecla Enter, proteger o rascunho e pré-visualizar formatação.",
    "base_congelada": "Congelados e imutáveis: o elemento único &lt;textarea&gt; com data-slot=\"textarea\"; cantos rounded-lg; borda border-input e fundo bg-transparent (dark bg-input/30); padding px-2.5 py-2; tipografia text-base / md:text-sm e cor de placeholder text-muted-foreground; altura mínima min-h-16 e o auto-dimensionamento field-sizing-content; foco (outline-none, focus-visible:border-ring + ring-3 ring-ring/50); estados disabled (cursor-not-allowed, opacity-50, bg-input/50); e o estado de erro via aria-invalid (border-destructive + ring destructive). Nenhuma capacidade altera fonte, cor, cantos ou espaçamento — só somam comportamento ou conteúdo ao redor.",
    "props": [],
    "capacidades": [
        {
            "id": "contador-de-comprimento",
            "nome": "Contador de comprimento",
            "descricao": "Mostra a contagem viva do texto abaixo do campo — por caracteres, palavras ou caracteres restantes.",
            "controle": "select",
            "opcoes": [
                "caracteres",
                "palavras",
                "caracteres restantes"
            ],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "limite-m-ximo",
            "nome": "Limite máximo",
            "descricao": "Define um teto de caracteres; ao atingir, impede digitar além (ou apenas alerta usando o token destructive já existente).",
            "controle": "number",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (number). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "comportamento-do-enter",
            "nome": "Comportamento do Enter",
            "descricao": "Escolhe a semântica da tecla Enter para caixas de mensagem/comentário: quebrar linha, enviar, ou enviar com Shift+Enter para quebrar.",
            "controle": "select",
            "opcoes": [
                "Enter quebra linha (padrão)",
                "Ctrl/Cmd+Enter envia",
                "Enter envia · Shift+Enter quebra"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "redimensionamento",
            "nome": "Redimensionamento",
            "descricao": "Controla a alça de resize do usuário: travado, só na vertical ou livre — sem tocar no auto-crescer da base.",
            "controle": "select",
            "opcoes": [
                "Travado",
                "Vertical",
                "Livre"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "teto-de-crescimento",
            "nome": "Teto de crescimento",
            "descricao": "Limita quantas linhas o campo auto-cresce antes de passar a rolar internamente, evitando que textos enormes empurrem a página.",
            "controle": "number",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (number). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "mensagem-de-ajuda-erro",
            "nome": "Mensagem de ajuda/erro",
            "descricao": "Texto descritivo de apoio ou erro junto ao campo, ligado por aria-describedby; o erro reaproveita o estado aria-invalid da base.",
            "controle": "text",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (text). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "rascunho-autom-tico",
            "nome": "Rascunho automático",
            "descricao": "Persiste o que foi digitado (localStorage por campo) e restaura ao reabrir, protegendo textos longos de perda acidental.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "pr-visualiza-o-markdown",
            "nome": "Pré-visualização Markdown",
            "descricao": "Adiciona alternância Editar/Pré-visualizar que renderiza o Markdown digitado numa aba de leitura, sem alterar o campo de edição.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ui/textarea; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:Textarea />",
    "exemplo_demo": "<div class=\"*:max-w-xs contents\">\n    <twig:Textarea placeholder=\"Type your message here.\" />\n</div>",
    "regras": {
        "faca": [
            "Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.",
            "Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.",
            "Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}."
        ],
        "nao_faca": [
            "Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.",
            "Não crie variante/fork para a mesma coisa — existe UMA base por componente.",
            "Não reimplemente o componente nem adicione toolchain Node."
        ]
    },
    "a11y": "foco (outline-none, focus-visible:border-ring + ring-3 ring-ring/50). e o estado de erro via aria-invalid (border-destructive + ring destructive)",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "textarea"
        }
    },
    "props_nota": "Sem props próprias declaradas: a base repassa atributos nativos via {{ attributes }} (id, name, type, value, aria-*, data-*). Props específicas, quando existem, ficam nos subcomponentes."
}