# Input

> Componentes / Formulário e ação · Estado: **Em andamento** — Implementado e utilizável, mas ainda sem a validação completa de acessibilidade.

Campo de texto de uma linha. A borda de 2px com foco diz se o que foi digitado já basta — cinza quando não há regra ou o dado ainda está incompleto (com ícone vermelho), verde com dado suficiente, vermelha só no erro de verdade —, sempre com ícone junto da cor.

- Função: Formulário e ação
- Também chamado de: campo de texto, caixa de texto, text field, input
- Mora em: `app/components/atoms/Input.tsx`
- Página: [https://phorte-design-system.vercel.app/styleguide/peca/Input](https://phorte-design-system.vercel.app/styleguide/peca/Input)
- Ficha no Storybook: [components-base-input](https://main--69b4847d498a9bd8659db9ba.chromatic.com/?path=/docs/components-base-input--docs)
- Prévia ao vivo, sozinha: [https://phorte-design-system.vercel.app/previa/Input/0](https://phorte-design-system.vercel.app/previa/Input/0)
- Todas as peças: [https://phorte-design-system.vercel.app/llms.txt](https://phorte-design-system.vercel.app/llms.txt)

## Como usar no código

```tsx
import { Input } from '@/app/components/atoms/Input';

<Input />
```

## Uso

### Variantes na ficha

1. [Padrão](https://phorte-design-system.vercel.app/previa/Input/0)
2. [Com valor](https://phorte-design-system.vercel.app/previa/Input/1)
3. [Estados](https://phorte-design-system.vercel.app/previa/Input/2)
4. [Senha de 6 dígitos](https://phorte-design-system.vercel.app/previa/Input/3)
5. [Erro](https://phorte-design-system.vercel.app/previa/Input/4)
6. [Somente leitura](https://phorte-design-system.vercel.app/previa/Input/5)
7. [Desabilitado](https://phorte-design-system.vercel.app/previa/Input/6)
8. [Senha](https://phorte-design-system.vercel.app/previa/Input/7)
9. [Com encaixes](https://phorte-design-system.vercel.app/previa/Input/8)
10. [Mostrar senha](https://phorte-design-system.vercel.app/previa/Input/9)

Para qualquer dado de uma linha — nome, e-mail, telefone, senha, CPF —, dentro de um FormField, que dá o rótulo, a dica e o erro.

### Quando não usar

Para texto longo, um `<textarea>`. Para escolher entre opções conhecidas, o Select ou o Radio. Para buscar, o SearchBar, que já é um formulário de busca.

### Faça

- Use dentro de um FormField: é ele que liga rótulo, dica e erro ao campo.
- Dê uma regra (`validar` ou `minLength`) quando houver critério de "pronto" — é ela que liga o vermelho e o verde.
- Use o `type` certo (`email`, `tel`, `password`): muda o teclado do celular.
- Largura e margem pela `className`; o `<input>` em si, por `classNameDoCampo`.

### Evite

- Usar o `placeholder` como rótulo: ele some quando a pessoa começa a digitar.
- Somar um `ring-` pela `className`: o foco já é a borda de 2px, e o anel extra é seu para conferir o contraste.
- Confundir `readOnly` com `disabled`: somente leitura é dado decidido que a pessoa vê e copia; desabilitado sai do Tab e não vai no envio.

### Nota de uso

**O foco é a borda de 2px**, e não um anel: cinza (`--border-focus`) com o campo vazio, vermelho com dado insuficiente, verde com dado suficiente — as quatro cores passam de 3:1. O contrato vem de `campoVariants`, o mesmo do `Select` e do `SearchBar`. Se você somar um `ring-` pela `className`, ele se soma à borda e é seu: confira o contraste contra o fundo. `error` pinta a borda **e** emite `aria-invalid`. A `className` vai para a caixa externa (largura, margem); para estilizar o `<input>` em si, use `classNameDoCampo`. **`readOnly` ou `disabled`:** somente leitura (tracejado) é para dado decidido que a pessoa precisa ver e copiar; desabilitado é para campo que não se aplica agora — sai da tabulação e não vai no envio.

### Anatomia

1. **Caixa** — Borda de controle, `px-4 py-3`, cantos `rounded-md`.
2. **Texto e placeholder** — `text-sm`; placeholder em `--muted-foreground`.
3. **Ícone de estado** — Só com regra: X vermelho (incompleto) ou visto verde (suficiente), à direita.
4. **Encaixes** — `inicio` e `fim` (ícone ou botão dentro da caixa); `limpavel` põe o botão de limpar; `type="password"` ganha "Mostrar senha". Todo botão tem 44×44.

### Estados

| Estado | O que muda |
| --- | --- |
| Padrão | Borda `--border-interactive` de 1px, vazio ou com dado. |
| Foco | Borda `--border-focus` de 2px (sombra interna, para o texto não pular). É o estado com foco quando não há regra ou o campo está vazio. |
| Incompleto | Com foco e regra não cumprida: a borda de foco (cinza, 2px) e o ícone X vermelho; anuncia "Ainda incompleto". Não é erro — o vermelho da borda fica para o erro vindo do formulário (E60). |
| Senha visível | "Mostrar senha" com `aria-pressed="true"`: o campo passa a `type="text"`. |
| Suficiente | Com foco e regra cumprida: borda verde de 2px e visto; anuncia "Completo". |
| Erro | `error` ou `aria-invalid`: borda vermelha, com ou sem foco, e `aria-invalid` no campo. |
| Somente leitura | `readOnly`: borda tracejada e fundo `--muted`. |
| Desabilitado | 50% de opacidade, cursor proibido, fora do Tab. |

### Comportamento

| Situação | O que acontece |
| --- | --- |
| Digitar com regra | O ícone X some e a borda fica verde quando a regra passa — conferido no teste da ficha: três dígitos num CEP de oito ficam incompletos, sem `aria-invalid`. |
| Esc num campo limpável | Limpa e mantém o foco; o botão de limpar some com o campo vazio — conferido no teste da ficha. |
| Sair do campo | A borda volta ao padrão (ou ao vermelho, se houver erro de fora). |
| Sem regra | Não há invólucro nem ícone: é o `<input>` de sempre. |
| Dentro de um FormField com erro | O `aria-invalid` chega pelo FormField, e a borda vermelha sai do mesmo atributo. |

### Relacionados

- [FormField](https://phorte-design-system.vercel.app/styleguide/md/FormField.md) — Rótulo, dica e erro ligados ao campo.
- [SearchBar](https://phorte-design-system.vercel.app/styleguide/md/SearchBar.md) — Campo de busca, já num formulário.
- [Select](https://phorte-design-system.vercel.app/styleguide/md/Select.md) — Para escolher entre opções conhecidas.

## Especificação

### Propriedades

| Prop | Tipo | Padrão | Obrigatória | Descrição |
| --- | --- | --- | --- | --- |
| `error` | `boolean` | — | não | Pinta a borda de erro **e** emite `aria-invalid`: vermelho e anúncio saem da mesma fonte. |
| `classNameDoCampo` | `string` | — | não | Classes do `<input>` em si. A `className` vai para a caixa externa — largura e margem —, como no `Select`. Sem encaixe nem regra não há invólucro, e as duas caem no mesmo elemento. |
| `validar` | `(valor: string) => boolean` | — | não | Quando o dado digitado já é suficiente. Ex.: `(v) => /\S+@\S+\.\S+/.test(v)`. Sem `validar`, o `minLength` serve de regra. |
| `inicio` | `React.ReactNode` | — | não | Ícone (decorativo, com `aria-hidden`) ou botão no começo da caixa. |
| `fim` | `React.ReactNode` | — | não | Ícone ou botão no fim da caixa. |
| `limpavel` | `boolean` | — | não | Botão de limpar quando há texto; o `Esc` também limpa. |
| `rotuloDoLimpar` | `string` | `'Limpar o campo'` | não | Nome do botão de limpar. Diga o que se limpa: "Limpar busca". |
| `onLimpar` | `() => void` | — | não | Chamado ao limpar. Num campo controlado, é aqui que o valor volta a vazio. |

Aceita também: `React.InputHTMLAttributes<HTMLInputElement>`.

### Medidas

| Parte | Valor | Classe |
| --- | --- | --- |
| Altura com texto `text-sm` | 46px (medido) | `px-4 py-3` |
| Borda com foco | 2px (1px de borda + 1px de sombra interna) | `shadow-[inset_0_0_0_1px_var(--border-focus)]` |

### Tokens de cor

| Token | Valor | Usado como |
| --- | --- | --- |
| `--destructive` | #BA0517 | `text-destructive` |
| `--foreground` | #1A1A1A | `text-ink` |
| `--muted` | #F4F6F9 | `bg-muted` |
| `--muted-foreground` | #595959 | `text-muted-foreground` |
| `--ring` | var(--footer-red)), decisão da revisão 2. Se outra peça precisar
   * dele como superfície, o nome vira de papel (ex.: --surface-brand-strong). */
  --footer-red: #7D1B1B | `ring-ring` |
| `--success` | #2E844A | `text-success` |

## Acessibilidade

### Teclado

| Tecla | Faz |
| --- | --- |
| Tab | Leva o foco ao campo; a borda engrossa. Desabilitado é pulado. |
| Letras | Digitam no campo; com regra, a borda e o anúncio mudam só quando o estado muda. |
| Enter | Dentro de um `<form>`, envia o formulário — comportamento nativo. |

### Leitor de tela

- O anúncio de "Ainda incompleto" e "Completo" é educado (`aria-live="polite"`) e só muda quando o estado muda — não a cada tecla.
- O erro de verdade — com mensagem — vem do FormField, por `aria-invalid` e `aria-describedby`.

### O que sustenta o estado

Usa os padrões: formulario, regiaoViva. Falta ouvir com leitor de tela: formulario, regiaoViva.

## Status e recursos

| Tipo | Recurso | Status |
| --- | --- | --- |
| Código | `app/components/atoms/Input.tsx` | Disponível |
| Ficha | https://main--69b4847d498a9bd8659db9ba.chromatic.com/?path=/docs/components-base-input--docs | Disponível |
| Teste de acessibilidade | axe em `npm run test` | No gate de todo commit |
| Leitor de tela | Passada da E33 | Falta ouvir |
| Design | Figma | Desativado até a Fase 2 |

### Histórico

- `0298af8` 2026-09-30 — E147: fechamento da Entrega 11 — REPORT.md, verificação cross-agent e o gate estático estrito
- Histórico completo no GitHub: https://github.com/srbunnie/Pos-Phorte-Design-System/commits/main/app/components/atoms/Input.tsx
